blob: 542e9d139b4e6b57e9a11d6f7318573d7e8cd0f1 [file] [log] [blame]
package annotate
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/golang/glog"
"skia.googlesource.com/buildbot.git/go/login"
"skia.googlesource.com/buildbot.git/go/util"
"skia.googlesource.com/buildbot.git/perf/go/activitylog"
"skia.googlesource.com/buildbot.git/perf/go/alerting"
"skia.googlesource.com/buildbot.git/perf/go/types"
)
// Handler serves the /annotate/ endpoint for changing the status of an
// alert cluster. It also writes a new types.Activity log record to the database.
//
// Expects a POST of JSON of the following form:
//
// {
// Id: 20 - The id of the alerting cluster.
// Status: "Ignore" - The new Status value.
// Message: "SKP Update" - The new Messge value.
// }
//
// Returns JSON of the form:
//
// {
// "Bug": "http://"
// }
//
// Where bug, if set, is the URL the user should be directed to to log a bug report.
func Handler(w http.ResponseWriter, r *http.Request) {
glog.Infof("Annotate Handler: %q\n", r.URL.Path)
if login.LoggedInAs(r) == "" {
util.ReportError(w, r, fmt.Errorf("Not logged in."), "You must be logged in to change an alert status.")
return
}
if r.Method != "POST" {
http.NotFound(w, r)
return
}
if r.Body == nil {
util.ReportError(w, r, fmt.Errorf("Missing POST Body."), "POST with no request body.")
return
}
req := struct {
Id int64
Status string
Message string
}{}
dec := json.NewDecoder(r.Body)
if err := dec.Decode(&req); err != nil {
util.ReportError(w, r, err, "Unable to decode posted JSON.")
return
}
if !util.In(req.Status, types.ValidStatusValues) {
util.ReportError(w, r, fmt.Errorf("Invalid status value: %s", req.Status), "Unknown value.")
return
}
// Store the updated values in the ClusterSummary.
c, err := alerting.Get(req.Id)
if err != nil {
util.ReportError(w, r, err, "Failed to load cluster summary.")
return
}
c.Status = req.Status
c.Message = req.Message
if err := alerting.Write(c); err != nil {
util.ReportError(w, r, err, "Failed to save cluster summary.")
return
}
// Write a new Activity record.
// TODO(jcgregorio) Move into alerting.Write().
a := &types.Activity{
UserID: login.LoggedInAs(r),
Action: "Perf Alert: " + req.Status,
URL: fmt.Sprintf("https://skiaperf.com/cl/%d", req.Id),
}
if err := activitylog.Write(a); err != nil {
util.ReportError(w, r, err, "Failed to save activity.")
return
}
retval := map[string]string{}
if req.Status == "Bug" {
q := url.Values{
"labels": []string{"FromSkiaPerf,Type-Defect,Priority-Medium"},
"comment": []string{fmt.Sprintf(`This bug was found via SkiaPerf.
Visit this URL to see the details of the suspicious cluster:
https://skiaperf.com/cl/%d.
Don't remove the above URL, it is used to match bugs to alerts.
`, req.Id)},
}
retval["Bug"] = "https://code.google.com/p/skia/issues/entry?" + q.Encode()
}
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
if err := enc.Encode(retval); err != nil {
util.ReportError(w, r, err, "Error while encoding annotation response.")
}
}