// commitsHandler handles requests for commits. // // Queries look like: // // /commits/?begin=hash1&end=hash2 // // or if there is only one hash: // // /commits/?begin=hash // // The response is HTML of the form: // // <pre> // commit <a href="http://skia.googlesource....">5bdbd13d8833d23e0da552f6817ae0b5a4e849e5</a> // Author: Joe Gregorio <*****@*****.**> // Date: Wed Aug 6 16:16:18 2014 -0400 // // Back to plotting lines. // // perf/go/skiaperf/perf.go // perf/go/types/types.go // perf/res/js/logic.js // // commit <a // ... // </pre> // func commitsHandler(w http.ResponseWriter, r *http.Request) { glog.Infof("Query Handler: %q\n", r.URL.Path) if r.Method != "GET" { http.NotFound(w, r) return } begin := r.FormValue("begin") if len(begin) != 40 { util.ReportError(w, r, fmt.Errorf("Invalid hash format: %s", begin), "Error while looking up hashes.") return } end := r.FormValue("end") body, err := git.Log(begin, end) if err != nil { util.ReportError(w, r, err, "Error while looking up hashes.") return } escaped := ehtml.EscapeString(body) linkified := commitLinkifyRe.ReplaceAllString(escaped, "<span class=subject>commit <a href=\"https://skia.googlesource.com/skia/+/${1}\" target=\"_blank\">${1}</a></span>") w.Header().Set("Content-Type", "text/plain") if _, err := w.Write([]byte(fmt.Sprintf("<pre>%s</pre>", linkified))); err != nil { glog.Errorf("Failed to write or encode output: %s", err) return } }
// changeHandler handles actions on individual services. // // The actions are forwarded off to the pulld service // running on the machine hosting that service. func changeHandler(w http.ResponseWriter, r *http.Request) { if login.LoggedInAs(r) == "" { util.ReportError(w, r, fmt.Errorf("You must be logged on to push."), "") return } if err := r.ParseForm(); err != nil { util.ReportError(w, r, err, "Failed to parse form.") return } action := r.Form.Get("action") name := r.Form.Get("name") machine := ip.Resolve(r.Form.Get("machine")) url := fmt.Sprintf("http://%s:10114/_/change?name=%s&action=%s", machine, name, action) resp, err := client.Post(url, "", nil) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to reach %s: %v %s", machine, resp, err)) return } defer util.Close(resp.Body) if resp.StatusCode != 200 { util.ReportError(w, r, err, fmt.Sprintf("Failed to reach %s: %v %s", machine, resp, err)) return } w.Header().Set("Content-Type", "application/json") if _, err := io.Copy(w, resp.Body); err != nil { glog.Errorf("Failed to copy JSON error out: %s", err) } }
func addCommitCommentHandler(w http.ResponseWriter, r *http.Request) { defer timer.New("addCommitCommentHandler").Stop() if !userHasEditRights(r) { util.ReportError(w, r, fmt.Errorf("User does not have edit rights."), "User does not have edit rights.") return } w.Header().Set("Content-Type", "application/json") commit := mux.Vars(r)["commit"] comment := struct { Comment string `json:"comment"` }{} if err := json.NewDecoder(r.Body).Decode(&comment); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to add comment: %v", err)) return } defer util.Close(r.Body) c := buildbot.CommitComment{ Commit: commit, User: login.LoggedInAs(r), Timestamp: time.Now().UTC(), Message: comment.Comment, } if err := db.PutCommitComment(&c); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to add commit comment: %v", err)) return } }
// redirectHandler handles redirecting to the correct tradefed page. func redirectHandler(w http.ResponseWriter, r *http.Request) { if login.LoggedInAs(r) == "" { r.Header.Set("Referer", r.URL.String()) http.Redirect(w, r, login.LoginURL(w, r), 302) return } vars := mux.Vars(r) codename := vars["codename"] buildNumberStr := vars["buildNumber"] target, err := codenameDB.Get([]byte(codename), nil) if err != nil { util.ReportError(w, r, err, "Not a valid target codename.") return } buildNumber, err := strconv.Atoi(buildNumberStr) if err != nil { util.ReportError(w, r, err, "Not a valid build number.") return } build, err := buildbot.GetBuildFromDB(string(codename), FAKE_MASTER, buildNumber) if err != nil { util.ReportError(w, r, err, "Could not find a matching build.") } id, ok := build.GetProperty("androidinternal_buildid").([]interface{})[1].(string) if !ok { util.ReportError(w, r, fmt.Errorf("Got %#v", id), "Could not find a matching build id.") return } w.Header().Set("Content-Type", "text/html") if _, err := w.Write([]byte(fmt.Sprintf(REDIRECT_TEMPLATE, codename, target, id, id, target, id, id, target))); err != nil { glog.Errorf("Failed to write response: %s", err) } }
// triageUndoHandler performs an "undo" for a given change id. // The change id's are returned in the result of polyTriageLogHandler. // It accepts one query parameter 'id' which is the id if the change // that should be reversed. // If successful it retunrs the same result as a call to polyTriageLogHandler // to reflect the changed triagelog. func triageUndoHandler(w http.ResponseWriter, r *http.Request) { // Get the user and make sure they are logged in. user := login.LoggedInAs(r) if user == "" { util.ReportError(w, r, fmt.Errorf("Not logged in."), "You must be logged in to change expectations.") return } // Extract the id to undo. changeID, err := strconv.Atoi(r.URL.Query().Get("id")) if err != nil { util.ReportError(w, r, err, "Invalid change id.") return } // Do the undo procedure. _, err = storages.ExpectationsStore.UndoChange(changeID, user) if err != nil { util.ReportError(w, r, err, "Unable to undo.") return } // Send the same response as a query for the first page. polyTriageLogHandler(w, r) }
func postAlertsJsonHandler(w http.ResponseWriter, r *http.Request) { // Get the alert ID. alertIdStr, ok := mux.Vars(r)["alertId"] if !ok { util.ReportError(w, r, fmt.Errorf("No alert ID provided."), "No alert ID provided.") return } alertId, err := strconv.ParseInt(alertIdStr, 10, 64) if err != nil { util.ReportError(w, r, fmt.Errorf("Invalid alert ID %s", alertIdStr), "Not found.") return } var req struct { Until int `json:"until"` Comment string `json:"comment"` } defer util.Close(r.Body) if err := json.NewDecoder(r.Body).Decode(&req); err != nil { util.ReportError(w, r, err, "Failed to decode request body.") return } handleAlert(alertId, req.Comment, req.Until, w, r) }
// clusteringHandler handles doing the actual k-means clustering. // // The return format is JSON of the form: // // { // "Clusters": [ // { // "Keys": [ // "x86:GeForce320M:MacMini4.1:Mac10.8:GM_varied_text_clipped_no_lcd_640_480:8888",...], // "ParamSummaries": [ // [{"Value": "Win8", "Weight": 15}, {"Value": "Android", "Weight": 14}, ...] // ], // "StepFit": { // "LeastSquares":0.0006582442047814354, // "TurningPoint":162, // "StepSize":0.023272272692293046, // "Regression": 35.3 // } // Traces: [[[0, -0.00007967326606768456], [1, 0.011877665949459049], [2, 0.012158129176717419],...]] // }, // ... // ], // "K": 5, // "StdDevThreshhold": 0.1 // } // // Note that Keys contains all the keys, while Traces only contains traces of // the N closest cluster members and the centroid. // // Takes the following query parameters: // // _k - The K to use for k-means clustering. // _stddev - The standard deviation to use when normalize traces // during k-means clustering. // _issue - The Rietveld issue ID with trybot results to include. // // Additionally the rest of the query parameters as returned from // sk.Query.selectionsAsQuery(). func clusteringHandler(w http.ResponseWriter, r *http.Request) { glog.Infof("Clustering Handler: %q\n", r.URL.Path) tile := masterTileBuilder.GetTile() w.Header().Set("Content-Type", "application/json") // If there are no query parameters just return with an empty set of ClusterSummaries. if r.FormValue("_k") == "" || r.FormValue("_stddev") == "" { writeClusterSummaries(clustering.NewClusterSummaries(), w, r) return } k, err := strconv.ParseInt(r.FormValue("_k"), 10, 32) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("_k parameter must be an integer %s.", r.FormValue("_k"))) return } stddev, err := strconv.ParseFloat(r.FormValue("_stddev"), 64) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("_stddev parameter must be a float %s.", r.FormValue("_stddev"))) return } issue := r.FormValue("_issue") var tryResults *types.TryBotResults = nil if issue != "" { var err error tryResults, err = trybot.Get(issue) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to get trybot data for clustering.")) return } } delete(r.Form, "_k") delete(r.Form, "_stddev") delete(r.Form, "_issue") // Create a filter function for traces that match the query parameters and // optionally tryResults. filter := func(key string, tr *types.PerfTrace) bool { if tryResults != nil { if _, ok := tryResults.Values[key]; !ok { return false } } return tiling.Matches(tr, r.Form) } if issue != "" { if tile, err = trybot.TileWithTryData(tile, issue); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to get trybot data for clustering.")) return } } summary, err := clustering.CalculateClusterSummaries(tile, int(k), stddev, filter) if err != nil { util.ReportError(w, r, err, "Failed to calculate clusters.") return } writeClusterSummaries(summary, w, r) }
// showcutHandler handles the POST requests of the shortcut page. // // Shortcuts are of the form: // // { // "scale": 0, // "tiles": [-1], // "hash": "a1092123890...", // "ids": [ // "x86:...", // "x86:...", // "x86:...", // ] // } // // hash - The git hash of where a step was detected. Can be null. // func shortcutHandler(w http.ResponseWriter, r *http.Request) { // TODO(jcgregorio): Add unit tests. match := shortcutHandlerPath.FindStringSubmatch(r.URL.Path) if match == nil { http.NotFound(w, r) return } if r.Method == "POST" { // check header if ct := r.Header.Get("Content-Type"); ct != "application/json" { util.ReportError(w, r, fmt.Errorf("Error: received %s", ct), "Invalid content type.") return } defer util.Close(r.Body) id, err := shortcut.Insert(r.Body) if err != nil { util.ReportError(w, r, err, "Error inserting shortcut.") return } w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) if err := enc.Encode(map[string]string{"id": id}); err != nil { glog.Errorf("Failed to write or encode output: %s", err) } } else { http.NotFound(w, r) } }
func deleteBuildCommentHandler(w http.ResponseWriter, r *http.Request) { defer timer.New("deleteBuildCommentHandler").Stop() if !userHasEditRights(r) { util.ReportError(w, r, fmt.Errorf("User does not have edit rights."), "User does not have edit rights.") return } w.Header().Set("Content-Type", "application/json") cache, err := getCommitCache(w, r) if err != nil { return } buildId, err := strconv.ParseInt(mux.Vars(r)["buildId"], 10, 32) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("Invalid build id: %v", err)) return } commentId, err := strconv.ParseInt(mux.Vars(r)["commentId"], 10, 32) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("Invalid comment id: %v", err)) return } if err := cache.DeleteBuildComment(int(buildId), int(commentId)); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to delete comment: %v", err)) return } }
// shortCommitsHandler returns basic info of a range of commits. // // Queries look like: // // /commits/?begin=hash1&end=hash2 // // Response is JSON of ShortCommits format that looks like: // // { // "commits": [ // { // hash: "123abc", // author: "bensong", // subject: "Adds short commits." // }, // ... // ] // } // func shortCommitsHandler(w http.ResponseWriter, r *http.Request) { glog.Infof("Query Handler: %q\n", r.URL.Path) if r.Method != "GET" { http.NotFound(w, r) return } begin := r.FormValue("begin") if len(begin) != 40 { util.ReportError(w, r, fmt.Errorf("Invalid begin hash format: %s", begin), "Error while looking up hashes.") return } end := r.FormValue("end") if len(end) != 40 { util.ReportError(w, r, fmt.Errorf("Invalid end hash format: %s", end), "Error while looking up hashes.") return } commits, err := git.ShortList(begin, end) if err != nil { util.ReportError(w, r, err, "Error while looking up hashes.") return } w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) if err := enc.Encode(commits); err != nil { glog.Errorf("Failed to write or encode output: %s", err) } }
func RedoTaskHandler(prototype Task, w http.ResponseWriter, r *http.Request) { if !ctfeutil.UserHasEditRights(r) { skutil.ReportError(w, r, fmt.Errorf("Must have google or chromium account to redo tasks"), "") return } w.Header().Set("Content-Type", "application/json") vars := struct{ Id int64 }{} if err := json.NewDecoder(r.Body).Decode(&vars); err != nil { skutil.ReportError(w, r, err, "Failed to parse redo request") return } defer skutil.Close(r.Body) rowQuery := fmt.Sprintf("SELECT * FROM %s WHERE id = ? AND ts_completed IS NOT NULL", prototype.TableName()) binds := []interface{}{vars.Id} data, err := prototype.Select(rowQuery, binds...) if err != nil { skutil.ReportError(w, r, err, "Unable to find requested task.") return } tasks := AsTaskSlice(data) if len(tasks) != 1 { skutil.ReportError(w, r, err, "Unable to find requested task.") return } addTaskVars := tasks[0].GetPopulatedAddTaskVars() // Replace the username with the new requester. addTaskVars.GetAddTaskCommonVars().Username = login.LoggedInAs(r) if _, err := AddTask(addTaskVars); err != nil { skutil.ReportError(w, r, err, "Could not redo the task.") return } }
func addBuildCommentHandler(w http.ResponseWriter, r *http.Request) { defer timer.New("addBuildCommentHandler").Stop() if !userHasEditRights(r) { util.ReportError(w, r, fmt.Errorf("User does not have edit rights."), "User does not have edit rights.") return } w.Header().Set("Content-Type", "application/json") cache, err := getCommitCache(w, r) if err != nil { return } buildId, err := strconv.ParseInt(mux.Vars(r)["buildId"], 10, 32) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("Invalid build id: %v", err)) return } comment := struct { Comment string `json:"comment"` }{} if err := json.NewDecoder(r.Body).Decode(&comment); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to add comment: %v", err)) return } defer util.Close(r.Body) c := buildbot.BuildComment{ BuildId: int(buildId), User: login.LoggedInAs(r), Timestamp: float64(time.Now().UTC().Unix()), Message: comment.Comment, } if err := cache.AddBuildComment(int(buildId), &c); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to add comment: %v", err)) return } }
// calcHandler handles requests for the form: // // /calc/?formula=filter("config=8888") // // Where the formula is any formula that parser.Eval() accepts. // // The response is the same format as queryHandler. func calcHandler(w http.ResponseWriter, r *http.Request) { glog.Infof("Calc Handler: %q\n", r.URL.Path) w.Header().Set("Content-Type", "application/json") tile := masterTileBuilder.GetTile() formula := r.FormValue("formula") var data interface{} = nil if r.FormValue("flat") == "true" { resp := &FlatQueryResponse{ Traces: []*types.PerfTrace{}, } if err := addFlatCalculatedTraces(resp, tile, formula); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed in /calc/ to evaluate formula.")) return } data = resp } else { resp := &QueryResponse{ Traces: []*tiling.TraceGUI{}, Hash: "", } if err := addCalculatedTraces(resp, tile, formula); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed in /calc/ to evaluate formula.")) return } data = resp } enc := json.NewEncoder(w) if err := enc.Encode(data); err != nil { glog.Errorf("Failed to write or encode output: %s", err) return } }
func getOldestPendingTaskHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") tasks, err := getAllPendingTasks() if err != nil { skutil.ReportError(w, r, err, fmt.Sprintf("Failed to get all pending tasks: %v", err)) return } var oldestTask task_common.Task for _, task := range tasks { if oldestTask == nil { oldestTask = task } else if oldestTask.GetCommonCols().TsAdded.Int64 < task.GetCommonCols().TsAdded.Int64 { oldestTask = task } } oldestTaskJsonRepr := map[string]task_common.Task{} if oldestTask != nil { oldestTaskJsonRepr[oldestTask.GetTaskName()] = oldestTask } if err := json.NewEncoder(w).Encode(oldestTaskJsonRepr); err != nil { skutil.ReportError(w, r, err, fmt.Sprintf("Failed to encode JSON: %v", err)) return } }
func handleAlerts(w http.ResponseWriter, r *http.Request, title string, categories []string, excludeCategories []string) { w.Header().Set("Content-Type", "text/html") // Don't use cached templates in testing mode. if *testing { reloadTemplates() } categoriesJson, err := json.Marshal(categories) if err != nil { util.ReportError(w, r, fmt.Errorf("Failed to encode JSON."), "Failed to encode JSON") } excludeJson, err := json.Marshal(excludeCategories) if err != nil { util.ReportError(w, r, fmt.Errorf("Failed to encode JSON."), "Failed to encode JSON") } inp := struct { Categories string ExcludeCategories string Title string }{ Categories: string(categoriesJson), ExcludeCategories: string(excludeJson), Title: title, } if err := alertsTemplate.Execute(w, inp); err != nil { glog.Errorf("Failed to write or encode output: %s", err) } }
// activityHandler serves the HTML for the /activitylog/ page. // // If an optional number n is appended to the path, returns the most recent n // activities. Otherwise returns the most recent 100 results. // func activityHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") if *local { loadTemplates() } match := activityHandlerPath.FindStringSubmatch(r.URL.Path) if r.Method != "GET" || match == nil || len(match) != 2 { http.NotFound(w, r) return } n := 100 if len(match[1]) > 0 { num, err := strconv.ParseInt(match[1], 10, 0) if err != nil { util.ReportError(w, r, err, "Failed parsing the given number.") return } n = int(num) } a, err := activitylog.GetRecent(n) if err != nil { util.ReportError(w, r, err, "Failed to retrieve activity.") return } if err := templates.ExecuteTemplate(w, "activitylog.html", a); err != nil { glog.Errorln("Failed to expand template:", err) } }
func detailedReportsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var f fuzz.FileFuzzReport if name := r.FormValue("name"); name != "" { var err error if f, err = fuzz.FindFuzzDetailForFuzz(name); err != nil { util.ReportError(w, r, err, "There was a problem fulfilling the request.") } } else { line, err := strconv.ParseInt(r.FormValue("line"), 10, 32) if err != nil { line = fcommon.UNKNOWN_LINE } if f, err = fuzz.FindFuzzDetails(r.FormValue("file"), r.FormValue("func"), int(line), r.FormValue("fuzz-type") == "binary"); err != nil { util.ReportError(w, r, err, "There was a problem fulfilling the request.") return } } if err := json.NewEncoder(w).Encode(f); err != nil { glog.Errorf("Failed to write or encode output: %s", err) return } }
func AddTaskHandler(w http.ResponseWriter, r *http.Request, task AddTaskVars) { if !ctfeutil.UserHasEditRights(r) { skutil.ReportError(w, r, fmt.Errorf("Must have google or chromium account to add tasks"), "") return } if task.IsAdminTask() && !ctfeutil.UserHasAdminRights(r) { skutil.ReportError(w, r, fmt.Errorf("Must be admin to add admin tasks; contact rmistry@"), "") return } w.Header().Set("Content-Type", "application/json") if err := json.NewDecoder(r.Body).Decode(&task); err != nil { skutil.ReportError(w, r, err, fmt.Sprintf("Failed to add %T task", task)) return } defer skutil.Close(r.Body) task.GetAddTaskCommonVars().Username = login.LoggedInAs(r) task.GetAddTaskCommonVars().TsAdded = ctutil.GetCurrentTs() if len(task.GetAddTaskCommonVars().Username) > 255 { skutil.ReportError(w, r, fmt.Errorf("Username is too long, limit 255 bytes"), "") return } if _, err := AddTask(task); err != nil { skutil.ReportError(w, r, err, fmt.Sprintf("Failed to insert %T task", task)) return } }
// clHandler serves the HTML for the /cl/<id> page. // // These are shortcuts to individual clusters. // // See alertingHandler for the JSON it uses. // func clHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") if *local { loadTemplates() } match := clHandlerPath.FindStringSubmatch(r.URL.Path) if r.Method != "GET" || match == nil || len(match) != 2 { http.NotFound(w, r) return } id, err := strconv.ParseInt(match[1], 10, 0) if err != nil { util.ReportError(w, r, err, "Failed parsing ID.") return } cl, err := alerting.Get(id) if err != nil { util.ReportError(w, r, err, "Failed to find cluster with that ID.") return } if err := templates.ExecuteTemplate(w, "cl.html", cl); err != nil { glog.Errorln("Failed to expand template:", err) } }
func getCLHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") detail, err := getCLDetail(r.FormValue("cl")) if err != nil { skutil.ReportError(w, r, err, "") return } if detail.Issue == 0 { // Return successful empty response, since the user could still be typing. if err := json.NewEncoder(w).Encode(map[string]interface{}{}); err != nil { skutil.ReportError(w, r, err, "Failed to encode JSON") } return } patch, err := getCLPatch(detail, 0) if err != nil { skutil.ReportError(w, r, err, "") return } clData, err := gatherCLData(detail, patch) if err != nil { skutil.ReportError(w, r, err, "") return } if err = json.NewEncoder(w).Encode(clData); err != nil { skutil.ReportError(w, r, err, "") return } }
// tileHandler accepts URIs like /tiles/0/1 // where the URI format is /tiles/<tile-scale>/<tile-number> // // It returns JSON of the form: // // { // tiles: [20], // scale: 0, // paramset: { // "os": ["Android", "ChromeOS", ..], // "arch": ["Arm7", "x86", ...], // }, // commits: [ // { // "commit_time": 140329432, // "hash": "0e03478100ea", // "author": "*****@*****.**", // "commit_msg": "The subject line of the commit.", // }, // ... // ], // ticks: [ // [1.5, "Mon"], // [3.5, "Tue"] // ], // skps: [ // 5, 13, 24 // ] // } // // Where skps are the commit indices where the SKPs were updated. // func tileHandler(w http.ResponseWriter, r *http.Request) { glog.Infof("Tile Handler: %q\n", r.URL.Path) handlerStart := time.Now() match := tileHandlerPath.FindStringSubmatch(r.URL.Path) if r.Method != "GET" || match == nil || len(match) != 3 { http.NotFound(w, r) return } tileScale, err := strconv.ParseInt(match[1], 10, 0) if err != nil { util.ReportError(w, r, err, "Failed parsing tile scale.") return } tileNumber, err := strconv.ParseInt(match[2], 10, 0) if err != nil { util.ReportError(w, r, err, "Failed parsing tile number.") return } glog.Infof("tile: %d %d", tileScale, tileNumber) tile, err := getTile(int(tileScale), int(tileNumber)) if err != nil { util.ReportError(w, r, err, "Failed retrieving tile.") return } guiTile := tiling.NewTileGUI(tile.Scale, tile.TileIndex) guiTile.Commits = tile.Commits guiTile.ParamSet = tile.ParamSet // SkpCommits goes out to the git repo, add caching if this turns out to be // slow. if skps, err := git.SkpCommits(tile); err != nil { guiTile.Skps = []int{} glog.Errorf("Failed to calculate skps: %s", err) } else { guiTile.Skps = skps } ts := []int64{} for _, c := range tile.Commits { if c.CommitTime != 0 { ts = append(ts, c.CommitTime) } } glog.Infof("%#v", ts) guiTile.Ticks = human.FlotTickMarks(ts) // Marshal and send marshaledResult, err := json.Marshal(guiTile) if err != nil { util.ReportError(w, r, err, "Failed to marshal JSON.") return } w.Header().Set("Content-Type", "application/json") _, err = w.Write(marshaledResult) if err != nil { glog.Errorf("Failed to write or encode output: %s", err) } glog.Infoln("Total handler time: ", time.Since(handlerStart).Nanoseconds()) }
// singleHandler is similar to /query/0/-1/traces?<param filters>, but takes an // optional commit hash and returns a single value for each trace at that commit, // or the latest value if a hash is not given or found. The resulting JSON is in // SingleResponse format that looks like: // // { // "traces": [ // { // val: 1.1, // params: {"os: "Android", ...} // }, // ... // ], // "hash": "abc123", // } // func singleHandler(w http.ResponseWriter, r *http.Request) { glog.Infof("Single Handler: %q\n", r.URL.Path) handlerStart := time.Now() match := singleHandlerPath.FindStringSubmatch(r.URL.Path) if r.Method != "GET" || match == nil || len(match) != 2 { http.NotFound(w, r) return } if err := r.ParseForm(); err != nil { util.ReportError(w, r, err, "Failed to parse query params.") } hash := match[1] tileNum, idx, err := git.TileAddressFromHash(hash, time.Time(ingester.BEGINNING_OF_TIME)) if err != nil { glog.Infof("Did not find hash '%s', use latest: %q.\n", hash, err) tileNum = -1 idx = -1 } glog.Infof("Hash: %s tileNum: %d, idx: %d\n", hash, tileNum, idx) tile, err := getTile(0, tileNum) if err != nil { util.ReportError(w, r, err, "Failed retrieving tile.") return } if idx < 0 { idx = len(tile.Commits) - 1 // Defaults to the last slice element. } glog.Infof("Tile: %d; Idx: %d\n", tileNum, idx) ret := SingleResponse{ Traces: []*SingleTrace{}, Hash: tile.Commits[idx].Hash, } for _, tr := range tile.Traces { if tiling.Matches(tr, r.Form) { v, err := vec.FillAt(tr.(*types.PerfTrace).Values, idx) if err != nil { util.ReportError(w, r, err, "Error while getting value at slice index.") return } t := &SingleTrace{ Val: v, Params: tr.Params(), } ret.Traces = append(ret.Traces, t) } } w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) if err := enc.Encode(ret); err != nil { glog.Errorf("Failed to write or encode output: %s", err) } glog.Infoln("Total handler time: ", time.Since(handlerStart).Nanoseconds()) }
// kernelJSONHandler returns the data needed for displaying Kernel Density Estimates. // // The return format is JSON of the form: // // { // "commit1": { // "key1": 1.234, // "key2": 1.235, // "key3": 1.230, // "key4": 1.239, // ... // }, // "commit2": { // "key1": 1.434, // "key2": 1.834, // "key3": 1.234, // "key4": 1.134, // ... // }, // missing: 5, // Number of missing values. // } // // Takes the following query parameters: // // commit1 - The hash for the first commit. // commit2 - The hash for the second commit. // query - A paramset in URI query format used to filter the results at each commit. // func kernelJSONHandler(w http.ResponseWriter, r *http.Request) { // TODO(jcgregorio) Determine the tile(s) to load based on the commit hashes, // possibly loading two different tiles, one for each hash. tile := masterTileBuilder.GetTile() commit1 := r.FormValue("commit1") commit2 := r.FormValue("commit2") // Calulate the indices where the commit falls in the tile. commit1Index := -1 commit2Index := -1 // Confirm that the two commits appear in the tile. for i, c := range tile.Commits { if c.Hash == commit1 { commit1Index = i } if c.Hash == commit2 { commit2Index = i } } if commit1Index == -1 || commit2Index == -1 { glog.Warningf("Commits %s[%d] %s[%d]", commit1, commit1Index, commit2, commit2Index) util.ReportError(w, r, fmt.Errorf("Failed to find commits in tile."), fmt.Sprintf("Failed to find commits in tile.")) return } w.Header().Set("Content-Type", "application/json") query := r.FormValue("query") q, err := url.ParseQuery(query) if err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to parse query parameters.")) return } ret := struct { Commit1 map[string]float64 `json:"commit1"` Commit2 map[string]float64 `json:"commit2"` Missing int32 `json:"missing"` }{ Commit1: map[string]float64{}, Commit2: map[string]float64{}, } for key, tr := range tile.Traces { if tiling.Matches(tr, q) { if tr.IsMissing(commit1Index) || tr.IsMissing(commit2Index) { ret.Missing += 1 continue } ret.Commit1[key] = tr.(*types.PerfTrace).Values[commit1Index] ret.Commit2[key] = tr.(*types.PerfTrace).Values[commit2Index] } } enc := json.NewEncoder(w) if err := enc.Encode(ret); err != nil { glog.Errorf("Failed to write or encode output: %s", err) return } }
// polyTriageHandler handles a request to change the triage status of one or more // digests of one test. // // It accepts a POST'd JSON serialization of PolyTriageRequest and updates // the expectations. func polyTriageHandler(w http.ResponseWriter, r *http.Request) { req := &PolyTriageRequest{} if err := parseJson(r, req); err != nil { util.ReportError(w, r, err, "Failed to parse JSON request.") return } glog.Infof("Triage request: %#v", req) user := login.LoggedInAs(r) if user == "" { util.ReportError(w, r, fmt.Errorf("Not logged in."), "You must be logged in to triage.") return } // Build the expecations change request from the list of digests passed in. digests := req.Digest // Or build the expectations change request from filter, query, and include. if req.All { exp, err := storages.ExpectationsStore.Get() if err != nil { util.ReportError(w, r, err, "Failed to load expectations.") return } e := exp.Tests[req.Test] ii, _, err := imgInfo(req.Filter, req.Query, req.Test, e, -1, req.Include, false, "", "", req.Head) digests = []string{} for _, d := range ii { digests = append(digests, d.Digest) } } // Label the digests. labelledDigests := map[string]types.Label{} for _, d := range digests { labelledDigests[d] = types.LabelFromString(req.Status) } tc := map[string]types.TestClassification{ req.Test: labelledDigests, } // Otherwise update the expectations directly. if err := storages.ExpectationsStore.AddChange(tc, user); err != nil { util.ReportError(w, r, err, "Failed to store the updated expectations.") return } w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) if err := enc.Encode(map[string]string{}); err != nil { glog.Errorf("Failed to write or encode result: %s", err) } }
func handleAlert(alertId int64, comment string, until int, w http.ResponseWriter, r *http.Request) { email := login.LoggedInAs(r) if !userHasEditRights(email) { util.ReportError(w, r, fmt.Errorf("User does not have edit rights."), "You must be logged in to an account with edit rights to do that.") return } action, ok := mux.Vars(r)["action"] if !ok { util.ReportError(w, r, fmt.Errorf("No action provided."), "No action provided.") return } if action == "dismiss" { glog.Infof("%s %d", action, alertId) if err := alertManager.Dismiss(alertId, email, comment); err != nil { util.ReportError(w, r, err, "Failed to dismiss alert.") return } return } else if action == "snooze" { if until == 0 { util.ReportError(w, r, fmt.Errorf("Invalid snooze time."), fmt.Sprintf("Invalid snooze time")) return } until := time.Unix(int64(until), 0) glog.Infof("%s %d until %v", action, alertId, until.String()) if err := alertManager.Snooze(alertId, until, email, comment); err != nil { util.ReportError(w, r, err, "Failed to snooze alert.") return } return } else if action == "unsnooze" { glog.Infof("%s %d", action, alertId) if err := alertManager.Unsnooze(alertId, email, comment); err != nil { util.ReportError(w, r, err, "Failed to unsnooze alert.") return } return } else if action == "addcomment" { if !StringIsInteresting(comment) { util.ReportError(w, r, fmt.Errorf("Invalid comment text."), comment) return } glog.Infof("%s %d: %s", action, alertId, comment) if err := alertManager.AddComment(alertId, email, comment); err != nil { util.ReportError(w, r, err, "Failed to add comment.") return } return } else { util.ReportError(w, r, fmt.Errorf("Invalid action %s", action), "The requested action is invalid.") return } }
func getOldestPendingTaskHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") oldestTask, err := GetOldestPendingTask() if err != nil { skutil.ReportError(w, r, err, "Failed to get oldest pending task") return } if err := EncodeTask(w, oldestTask); err != nil { skutil.ReportError(w, r, err, fmt.Sprintf("Failed to encode JSON for %#v", oldestTask)) return } }
func perfJsonHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]interface{}{"alerts": perfStatus}); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to report Perf status: %v", err)) return } }
func polySearchJSONHandler(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { util.ReportError(w, r, err, "Invalid request.") return } di, err := summaries.Search(r.FormValue("query"), r.FormValue("include") == "true", r.FormValue("head") == "true", r.FormValue("pos") == "true", r.FormValue("neg") == "true", r.FormValue("unt") == "true") if err != nil { util.ReportError(w, r, err, "Search failed.") return } w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) if err := enc.Encode(di); err != nil { glog.Errorf("Failed to write or encode result: %s", err) } }
func autoRollJsonHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(rollStatus); err != nil { util.ReportError(w, r, err, fmt.Sprintf("Failed to report AutoRoll status: %v", err)) return } }
// polyTriageLogHandler returns the entries in the triagelog paginated // in reverse chronological order. func polyTriageLogHandler(w http.ResponseWriter, r *http.Request) { // Get the pagination params. var logEntries []*expstorage.TriageLogEntry var total int q := r.URL.Query() offset, size, err := util.PaginationParams(q, 0, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE) if err == nil { details := q.Get("details") == "true" logEntries, total, err = storages.ExpectationsStore.QueryLog(offset, size, details) } if err != nil { util.ReportError(w, r, err, "Unable to retrieve triage log.") return } pagination := &util.ResponsePagination{ Offset: offset, Size: size, Total: total, } sendResponse(w, logEntries, http.StatusOK, pagination) }