Beispiel #1
0
// 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
	}
}
Beispiel #2
0
// 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)
	}
}
Beispiel #3
0
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
	}
}
Beispiel #4
0
// 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)
	}
}
Beispiel #5
0
// 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)
}
Beispiel #6
0
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)
}
Beispiel #7
0
// 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)
}
Beispiel #8
0
// 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)
	}
}
Beispiel #9
0
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
	}
}
Beispiel #10
0
// 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
	}
}
Beispiel #12
0
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
	}
}
Beispiel #13
0
// 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
	}
}
Beispiel #15
0
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)
	}
}
Beispiel #16
0
// 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)
	}
}
Beispiel #17
0
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
	}
}
Beispiel #19
0
// 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
	}
}
Beispiel #21
0
// 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())
}
Beispiel #22
0
// 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())
}
Beispiel #23
0
// 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
	}
}
Beispiel #24
0
// 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)
	}
}
Beispiel #25
0
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
	}
}
Beispiel #27
0
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
	}
}
Beispiel #28
0
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)
	}
}
Beispiel #29
0
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
	}
}
Beispiel #30
0
// 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)
}