mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-30 23:00:29 +00:00
dashboard/app: remember job links
Remember link for jobs. Check that mailing list is in CC when we accept commands.
This commit is contained in:
parent
bf82068946
commit
e93131fcce
@ -155,19 +155,7 @@ report1
|
||||
}
|
||||
|
||||
// Now upstream the bug and check that it reaches the next reporting.
|
||||
incoming2 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
|
||||
Date: Tue, 15 Aug 2017 14:59:00 -0700
|
||||
Message-ID: <1234>
|
||||
Subject: crash1
|
||||
From: foo@bar.com
|
||||
To: foo@bar.com
|
||||
Cc: %v
|
||||
Content-Type: text/plain
|
||||
|
||||
#syz upstream
|
||||
`, sender0)
|
||||
|
||||
c.expectOK(c.POST("/_ah/mail/", incoming2))
|
||||
c.incomingEmail(sender0, "#syz upstream")
|
||||
|
||||
sender1 := ""
|
||||
{
|
||||
@ -317,18 +305,8 @@ unknown command "bad-command"
|
||||
}
|
||||
}
|
||||
|
||||
// Now mark the bug as invalid.
|
||||
incoming5 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
|
||||
Date: Tue, 15 Aug 2017 14:59:00 -0700
|
||||
Message-ID: <abcdef>
|
||||
Subject: title1
|
||||
From: foo@bar.com
|
||||
To: %v
|
||||
Content-Type: text/plain
|
||||
|
||||
#syz fix: some: commit title
|
||||
`, sender1)
|
||||
c.expectOK(c.POST("/_ah/mail/", incoming5))
|
||||
// Now mark the bug as fixed.
|
||||
c.incomingEmail(sender1, "#syz fix: some: commit title")
|
||||
c.expectOK(c.GET("/email_poll"))
|
||||
c.expectEQ(len(c.emailSink), 0)
|
||||
|
||||
@ -410,17 +388,7 @@ func TestEmailDup(t *testing.T) {
|
||||
msg2 := <-c.emailSink
|
||||
|
||||
// Dup crash2 to crash1.
|
||||
incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
|
||||
Date: Tue, 15 Aug 2017 14:59:00 -0700
|
||||
Message-ID: <12345>
|
||||
Subject: title1
|
||||
From: foo@bar.com
|
||||
To: %v
|
||||
Content-Type: text/plain
|
||||
|
||||
#syz dup: BUG: slightly more elaborate title
|
||||
`, msg2.Sender)
|
||||
c.expectOK(c.POST("/_ah/mail/", incoming1))
|
||||
c.incomingEmail(msg2.Sender, "#syz dup: BUG: slightly more elaborate title")
|
||||
c.expectOK(c.GET("/email_poll"))
|
||||
c.expectEQ(len(c.emailSink), 0)
|
||||
|
||||
@ -431,17 +399,7 @@ Content-Type: text/plain
|
||||
c.expectEQ(len(c.emailSink), 0)
|
||||
|
||||
// Now close the original bug, and check that new bugs for dup are now created.
|
||||
incoming2 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
|
||||
Date: Tue, 15 Aug 2017 14:59:00 -0700
|
||||
Message-ID: <12345>
|
||||
Subject: title1
|
||||
From: foo@bar.com
|
||||
To: %v
|
||||
Content-Type: text/plain
|
||||
|
||||
#syz invalid
|
||||
`, msg1.Sender)
|
||||
c.expectOK(c.POST("/_ah/mail/", incoming2))
|
||||
c.incomingEmail(msg1.Sender, "#syz invalid")
|
||||
|
||||
// New crash must produce new bug in the first reporting.
|
||||
c.expectOK(c.API(client2, key2, "report_crash", crash2, nil))
|
||||
|
@ -106,6 +106,7 @@ type Job struct {
|
||||
User string
|
||||
Reporting string
|
||||
ExtID string // email Message-ID
|
||||
Link string // web link for the job (e.g. email in the group)
|
||||
Namespace string
|
||||
Manager string
|
||||
BugTitle string
|
||||
|
@ -38,8 +38,7 @@ func addTestJob(c context.Context, bugID, user, extID, patch, repo, branch strin
|
||||
if err != nil {
|
||||
return "can't find associated bug", err
|
||||
}
|
||||
now := timeNow(c)
|
||||
bugReporting, _ := bugReportingByID(bug, bugID, now)
|
||||
bugReporting, _ := bugReportingByID(bug, bugID)
|
||||
|
||||
// TODO(dvyukov): find the exact crash that we reported.
|
||||
crash, crashKey, err := findCrashForBug(c, bug)
|
||||
@ -81,7 +80,7 @@ func addTestJob(c context.Context, bugID, user, extID, patch, repo, branch strin
|
||||
}
|
||||
|
||||
job := &Job{
|
||||
Created: now,
|
||||
Created: timeNow(c),
|
||||
User: user,
|
||||
Reporting: bugReporting.Name,
|
||||
ExtID: extID,
|
||||
@ -124,6 +123,32 @@ func addTestJob(c context.Context, bugID, user, extID, patch, repo, branch strin
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func updateTestJob(c context.Context, extID, link string) error {
|
||||
var jobs []*Job
|
||||
keys, err := datastore.NewQuery("Job").
|
||||
Filter("ExtID=", extID).
|
||||
GetAll(c, &jobs)
|
||||
if len(jobs) != 1 || err != nil {
|
||||
return fmt.Errorf("failed to query jobs: jobs=%v err=%v", len(jobs), err)
|
||||
}
|
||||
job, jobKey := jobs[0], keys[0]
|
||||
if job.Link != "" {
|
||||
return nil
|
||||
}
|
||||
tx := func(c context.Context) error {
|
||||
job := new(Job)
|
||||
if err := datastore.Get(c, jobKey, job); err != nil {
|
||||
return err
|
||||
}
|
||||
job.Link = link
|
||||
if _, err := datastore.Put(c, jobKey, job); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return datastore.RunInTransaction(c, tx, nil)
|
||||
}
|
||||
|
||||
// pollPendingJobs returns the next job to execute for the provided list of managers.
|
||||
func pollPendingJobs(c context.Context, managers []string) (interface{}, error) {
|
||||
retry:
|
||||
@ -318,7 +343,7 @@ func createBugReportForJob(c context.Context, job *Job, jobKey *datastore.Key, c
|
||||
Config: reportingConfig,
|
||||
ID: bugReporting.ID,
|
||||
JobID: extJobID(jobKey),
|
||||
ExtID: bugReporting.ExtID,
|
||||
ExtID: job.ExtID,
|
||||
Title: bug.displayTitle(),
|
||||
Log: crashLog,
|
||||
Report: report,
|
||||
|
@ -77,6 +77,7 @@ type uiCrash struct {
|
||||
|
||||
type uiJob struct {
|
||||
Created time.Time
|
||||
Link string
|
||||
User string
|
||||
Reporting string
|
||||
Namespace string
|
||||
@ -298,6 +299,7 @@ func loadRecentJobs(c context.Context) ([]*uiJob, error) {
|
||||
for i, job := range jobs {
|
||||
ui := &uiJob{
|
||||
Created: job.Created,
|
||||
Link: job.Link,
|
||||
User: job.User,
|
||||
Reporting: job.Reporting,
|
||||
Namespace: job.Namespace,
|
||||
|
@ -55,7 +55,7 @@
|
||||
</tr>
|
||||
{{range $job := $.Jobs}}
|
||||
<tr>
|
||||
<td class="time">{{formatTime $job.Created}}</td>
|
||||
<td class="time">{{if $job.Link}}<a href="{{$job.Link}}">{{formatTime $job.Created}}</a>{{else}}{{formatTime $job.Created}}{{end}}</td>
|
||||
<td class="time">{{formatTime $job.Started}}{{if gt $job.Attempts 1}} ({{$job.Attempts}}){{end}}</td>
|
||||
<td class="time">{{formatTime $job.Finished}}</td>
|
||||
<td>{{$job.User}}</td>
|
||||
|
@ -244,13 +244,9 @@ func createBugReport(c context.Context, bug *Bug, crash *Crash, bugReporting *Bu
|
||||
func incomingCommand(c context.Context, cmd *dashapi.BugUpdate) (bool, string, error) {
|
||||
log.Infof(c, "got command: %+q", cmd)
|
||||
ok, reason, err := incomingCommandImpl(c, cmd)
|
||||
if !ok {
|
||||
log.Errorf(c, "%v", reason)
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf(c, "%v", err)
|
||||
}
|
||||
if !ok {
|
||||
log.Errorf(c, "%v (%v)", reason, err)
|
||||
} else if !ok {
|
||||
log.Warningf(c, "invalid update: %v", reason)
|
||||
}
|
||||
return ok, reason, err
|
||||
@ -264,7 +260,7 @@ func incomingCommandImpl(c context.Context, cmd *dashapi.BugUpdate) (bool, strin
|
||||
now := timeNow(c)
|
||||
dupHash := ""
|
||||
if cmd.Status == dashapi.BugStatusDup {
|
||||
bugReporting, _ := bugReportingByID(bug, cmd.ID, now)
|
||||
bugReporting, _ := bugReportingByID(bug, cmd.ID)
|
||||
dup, dupKey, err := findBugByReportingID(c, cmd.DupOf)
|
||||
if err != nil {
|
||||
// Email reporting passes bug title in cmd.DupOf, try to find bug by title.
|
||||
@ -279,7 +275,7 @@ func incomingCommandImpl(c context.Context, cmd *dashapi.BugUpdate) (bool, strin
|
||||
}
|
||||
cmd.DupOf = dupReporting.ID
|
||||
}
|
||||
dupReporting, _ := bugReportingByID(dup, cmd.DupOf, now)
|
||||
dupReporting, _ := bugReportingByID(dup, cmd.DupOf)
|
||||
if bugReporting == nil || dupReporting == nil {
|
||||
return false, internalError, fmt.Errorf("can't find bug reporting")
|
||||
}
|
||||
@ -356,7 +352,7 @@ func incomingCommandTx(c context.Context, now time.Time, cmd *dashapi.BugUpdate,
|
||||
default:
|
||||
return false, internalError, fmt.Errorf("unknown bug status %v", bug.Status)
|
||||
}
|
||||
bugReporting, final := bugReportingByID(bug, cmd.ID, now)
|
||||
bugReporting, final := bugReportingByID(bug, cmd.ID)
|
||||
if bugReporting == nil {
|
||||
return false, internalError, fmt.Errorf("can't find bug reporting")
|
||||
}
|
||||
@ -492,12 +488,11 @@ func findDupByTitle(c context.Context, ns, title string) (*Bug, *datastore.Key,
|
||||
return bug, bugKey, nil
|
||||
}
|
||||
|
||||
func bugReportingByID(bug *Bug, id string, now time.Time) (*BugReporting, bool) {
|
||||
func bugReportingByID(bug *Bug, id string) (*BugReporting, bool) {
|
||||
for i := range bug.Reporting {
|
||||
if bug.Reporting[i].ID == id {
|
||||
return &bug.Reporting[i], i == len(bug.Reporting)-1
|
||||
}
|
||||
bug.Reporting[i].Closed = now
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
@ -224,15 +224,45 @@ func incomingMail(c context.Context, r *http.Request) error {
|
||||
}
|
||||
log.Infof(c, "received email: subject %q, from %q, cc %q, msg %q, bug %q, cmd %q, link %q",
|
||||
msg.Subject, msg.From, msg.Cc, msg.MessageID, msg.BugID, msg.Command, msg.Link)
|
||||
// A mailing list can send us a duplicate email, to not process/reply to such duplicate emails,
|
||||
// we ignore emails coming from our mailing lists. We could ignore only the mailing list
|
||||
// associated with the bug, but we don't know the bug reporting yet (we only have bug ID).
|
||||
if msg.Command != "" && mailingLists[email.CanonicalEmail(msg.From)] {
|
||||
bug, bugReporting, reporting := loadBugInfo(c, msg)
|
||||
if bug == nil {
|
||||
return nil // error was already logged
|
||||
}
|
||||
_ = bugReporting
|
||||
emailConfig := reporting.Config.(*EmailConfig)
|
||||
mailingList := email.CanonicalEmail(emailConfig.Email)
|
||||
fromMailingList := email.CanonicalEmail(msg.From) == mailingList
|
||||
mailingListInCC := checkMailingListInCC(c, msg, mailingList)
|
||||
// A mailing list can send us a duplicate email, to not process/reply
|
||||
// to such duplicate emails, we ignore emails coming from our mailing lists.
|
||||
if msg.Command == "test:" {
|
||||
if fromMailingList {
|
||||
if msg.Link != "" {
|
||||
if err := updateTestJob(c, msg.MessageID, msg.Link); err != nil {
|
||||
log.Errorf(c, "failed to update job: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
args := strings.Split(msg.CommandArgs, " ")
|
||||
if len(args) != 2 {
|
||||
return replyTo(c, msg, fmt.Sprintf("want 2 args (repo, branch), got %v",
|
||||
len(args)), nil)
|
||||
}
|
||||
reply := handleTestRequest(c, msg.BugID, email.CanonicalEmail(msg.From),
|
||||
msg.MessageID, msg.Patch, args[0], args[1])
|
||||
if reply != "" {
|
||||
return replyTo(c, msg, reply, nil)
|
||||
}
|
||||
if !mailingListInCC {
|
||||
warnMailingListInCC(c, msg, mailingList)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if fromMailingList && msg.Command != "" {
|
||||
log.Infof(c, "duplicate email from mailing list, ignoring")
|
||||
return nil
|
||||
}
|
||||
// TODO(dvyukov): check that our mailing list is in CC
|
||||
// (otherwise there will be no history of what hsppened with a bug).
|
||||
cmd := &dashapi.BugUpdate{
|
||||
ID: msg.BugID,
|
||||
ExtID: msg.MessageID,
|
||||
@ -258,19 +288,6 @@ func incomingMail(c context.Context, r *http.Request) error {
|
||||
}
|
||||
cmd.Status = dashapi.BugStatusDup
|
||||
cmd.DupOf = msg.CommandArgs
|
||||
case "test:":
|
||||
// TODO(dvyukov): remember email link for jobs.
|
||||
args := strings.Split(msg.CommandArgs, " ")
|
||||
if len(args) != 2 {
|
||||
return replyTo(c, msg, fmt.Sprintf("want 2 args (repo, branch), got %v",
|
||||
len(args)), nil)
|
||||
}
|
||||
reply := handleTestRequest(c, msg.BugID, email.CanonicalEmail(msg.From),
|
||||
msg.MessageID, msg.Patch, args[0], args[1])
|
||||
if reply != "" {
|
||||
return replyTo(c, msg, reply, nil)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return replyTo(c, msg, fmt.Sprintf("unknown command %q", msg.Command), nil)
|
||||
}
|
||||
@ -281,9 +298,66 @@ func incomingMail(c context.Context, r *http.Request) error {
|
||||
if !ok && reply != "" {
|
||||
return replyTo(c, msg, reply, nil)
|
||||
}
|
||||
if !mailingListInCC && msg.Command != "" {
|
||||
warnMailingListInCC(c, msg, mailingList)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadBugInfo(c context.Context, msg *email.Email) (bug *Bug, bugReporting *BugReporting, reporting *Reporting) {
|
||||
if msg.BugID == "" {
|
||||
log.Warningf(c, "no bug ID")
|
||||
return nil, nil, nil
|
||||
}
|
||||
bug, _, err := findBugByReportingID(c, msg.BugID)
|
||||
if err != nil {
|
||||
log.Errorf(c, "can't find bug: %v", err)
|
||||
replyTo(c, msg, "Can't find the corresponding bug.", nil)
|
||||
return nil, nil, nil
|
||||
}
|
||||
bugReporting, _ = bugReportingByID(bug, msg.BugID)
|
||||
if bugReporting == nil {
|
||||
log.Errorf(c, "can't find bug reporting: %v", err)
|
||||
replyTo(c, msg, "Can't find the corresponding bug.", nil)
|
||||
return nil, nil, nil
|
||||
}
|
||||
reporting = config.Namespaces[bug.Namespace].ReportingByName(bugReporting.Name)
|
||||
if reporting == nil {
|
||||
log.Errorf(c, "can't find reporting for this bug: namespace=%q reporting=%q",
|
||||
bug.Namespace, bugReporting.Name)
|
||||
return nil, nil, nil
|
||||
}
|
||||
if reporting.Config.Type() != emailType {
|
||||
log.Errorf(c, "reporting is not email: namespace=%q reporting=%q config=%q",
|
||||
bug.Namespace, bugReporting.Name, reporting.Config.Type())
|
||||
return nil, nil, nil
|
||||
}
|
||||
return bug, bugReporting, reporting
|
||||
}
|
||||
|
||||
func checkMailingListInCC(c context.Context, msg *email.Email, mailingList string) bool {
|
||||
if email.CanonicalEmail(msg.From) == mailingList {
|
||||
return true
|
||||
}
|
||||
for _, cc := range msg.Cc {
|
||||
if email.CanonicalEmail(cc) == mailingList {
|
||||
return true
|
||||
}
|
||||
}
|
||||
msg.Cc = append(msg.Cc, mailingList)
|
||||
return false
|
||||
}
|
||||
|
||||
func warnMailingListInCC(c context.Context, msg *email.Email, mailingList string) {
|
||||
reply := fmt.Sprintf("Your '%v' command is accepted, but please keep %v mailing list"+
|
||||
" in CC next time. It serves as a history of what happened with each bug report."+
|
||||
" Thank you.",
|
||||
msg.Command, mailingList)
|
||||
if err := replyTo(c, msg, reply, nil); err != nil {
|
||||
log.Errorf(c, "failed to send email reply: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var mailTemplates = template.Must(template.New("").ParseGlob("mail_*.txt"))
|
||||
|
||||
func sendMailTemplate(c context.Context, subject, from string, to []string, replyTo string,
|
||||
|
@ -175,6 +175,7 @@ Date: Tue, 15 Aug 2017 14:59:00 -0700
|
||||
Message-ID: <1234>
|
||||
Subject: crash1
|
||||
From: default@sender.com
|
||||
Cc: test@syzkaller.com, bugs@syzkaller.com
|
||||
To: %v
|
||||
Content-Type: text/plain
|
||||
|
||||
|
@ -41,6 +41,8 @@ reliable because of email clients splitting lines and messing with whitespaces.
|
||||
Note: if the crash happens again, it will cause creation of a new bug report.
|
||||
|
||||
Note: all commands must start from beginning of the line.
|
||||
Note: please keep `syzkaller-bugs@googlegroups.com` mailing list in CC.
|
||||
It serves as a history of what happened with each bug report.
|
||||
|
||||
## syzkaller reproducers
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user