// loadAlertContext fetches details from the database for all documents that are associated with the // AlertRequest. For example, it populates the task/build/version/project using the // task/build/version/project ids in the alert request document. func (qp *QueueProcessor) loadAlertContext(a *alert.AlertRequest) (*AlertContext, error) { aCtx := &AlertContext{AlertRequest: a} aCtx.Settings = qp.config taskId, projectId, buildId, versionId := a.TaskId, a.ProjectId, a.BuildId, a.VersionId patchId := a.PatchId var err error if len(a.HostId) > 0 { aCtx.Host, err = host.FindOne(host.ById(a.HostId)) if err != nil { return nil, err } } // Fetch task if there's a task ID present; if we find one, populate build/version IDs from it if len(taskId) > 0 { aCtx.Task, err = model.FindTask(taskId) if err != nil { return nil, err } if aCtx.Task != nil && aCtx.Task.Execution != a.Execution { oldTaskId := fmt.Sprintf("%s_%v", taskId, a.Execution) aCtx.Task, err = model.FindOneOldTask(bson.M{"_id": oldTaskId}, db.NoProjection, db.NoSort) if err != nil { return nil, err } } if aCtx.Task != nil { // override build and version ID with the ones this task belongs to buildId = aCtx.Task.BuildId versionId = aCtx.Task.Version projectId = aCtx.Task.Project aCtx.FailedTests = []model.TestResult{} for _, test := range aCtx.Task.TestResults { if test.Status == "fail" { aCtx.FailedTests = append(aCtx.FailedTests, test) } } } } // Fetch build if there's a build ID present; if we find one, populate version ID from it if len(buildId) > 0 { aCtx.Build, err = build.FindOne(build.ById(buildId)) if err != nil { return nil, err } if aCtx.Build != nil { versionId = aCtx.Build.Version projectId = aCtx.Build.Project } } if len(versionId) > 0 { aCtx.Version, err = version.FindOne(version.ById(versionId)) if err != nil { return nil, err } if aCtx.Version != nil { projectId = aCtx.Version.Identifier } } if len(patchId) > 0 { if !patch.IsValidId(patchId) { return nil, fmt.Errorf("patch id '%v' is not an object id", patchId) } aCtx.Patch, err = patch.FindOne(patch.ById(patch.NewId(patchId)).Project(patch.ExcludePatchDiff)) } else if aCtx.Version != nil { // patch isn't in URL but the version in context has one, get it aCtx.Patch, err = patch.FindOne(patch.ByVersion(aCtx.Version.Id).Project(patch.ExcludePatchDiff)) } // If there's a finalized patch loaded into context but not a version, load the version // associated with the patch as the context's version. if aCtx.Version == nil && aCtx.Patch != nil && aCtx.Patch.Version != "" { aCtx.Version, err = version.FindOne(version.ById(aCtx.Patch.Version).WithoutFields(version.ConfigKey)) if err != nil { return nil, err } } if len(projectId) > 0 { aCtx.ProjectRef, err = qp.findProject(projectId) if err != nil { return nil, err } } return aCtx, nil }
func (uis *UIServer) taskPage(w http.ResponseWriter, r *http.Request) { projCtx := MustHaveProjectContext(r) if projCtx.Task == nil { http.Error(w, "Not found", http.StatusNotFound) return } if projCtx.Build == nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("build not found")) return } if projCtx.Version == nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("version not found")) return } if projCtx.ProjectRef == nil { evergreen.Logger.Logf(slogger.ERROR, "Project ref is nil") uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("version not found")) return } executionStr := mux.Vars(r)["execution"] archived := false if executionStr != "" { // otherwise we can look in either tasks or old_tasks // where tasks are looked up in the old_tasks collection with key made up of // the original key and the execution number joined by an "_" // and the tasks are looked up in the tasks collection by key and execution // number, so that we avoid finding the wrong execution in the tasks // collection execution, err := strconv.Atoi(executionStr) if err != nil { http.Error(w, fmt.Sprintf("Bad execution number: %v", executionStr), http.StatusBadRequest) return } oldTaskId := fmt.Sprintf("%v_%v", projCtx.Task.Id, executionStr) taskFromDb, err := model.FindOneOldTask(bson.M{"_id": oldTaskId}, db.NoProjection, db.NoSort) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, err) return } archived = true if taskFromDb == nil { // for backwards compatibility with tasks without an execution if execution == 0 { taskFromDb, err = model.FindOneTask(bson.M{ "$and": []bson.M{ bson.M{"_id": projCtx.Task.Id}, bson.M{"$or": []bson.M{bson.M{"execution": 0}, bson.M{"execution": nil}}}}}, db.NoProjection, db.NoSort) } else { taskFromDb, err = model.FindOneTask(bson.M{"_id": projCtx.Task.Id, "execution": execution}, db.NoProjection, db.NoSort) } if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finding old task: %v", err)) return } } projCtx.Task = taskFromDb } // Build a struct containing the subset of task data needed for display in the UI task := uiTaskData{ Id: projCtx.Task.Id, DisplayName: projCtx.Task.DisplayName, Revision: projCtx.Task.Revision, Status: projCtx.Task.Status, TaskEndDetails: projCtx.Task.Details, Distro: projCtx.Task.DistroId, BuildVariant: projCtx.Task.BuildVariant, BuildId: projCtx.Task.BuildId, Activated: projCtx.Task.Activated, Restarts: projCtx.Task.Restarts, Execution: projCtx.Task.Execution, Requester: projCtx.Task.Requester, StartTime: projCtx.Task.StartTime.UnixNano(), DispatchTime: projCtx.Task.DispatchTime.UnixNano(), FinishTime: projCtx.Task.FinishTime.UnixNano(), ExpectedDuration: projCtx.Task.ExpectedDuration, PushTime: projCtx.Task.PushTime, TimeTaken: projCtx.Task.TimeTaken, Priority: projCtx.Task.Priority, TestResults: projCtx.Task.TestResults, Aborted: projCtx.Task.Aborted, CurrentTime: time.Now().UnixNano(), BuildVariantDisplay: projCtx.Build.DisplayName, Message: projCtx.Version.Message, Project: projCtx.Version.Identifier, Author: projCtx.Version.Author, AuthorEmail: projCtx.Version.AuthorEmail, VersionId: projCtx.Version.Id, RepoOwner: projCtx.ProjectRef.Owner, Repo: projCtx.ProjectRef.Repo, Archived: archived, } deps, taskWaiting, err := getTaskDependencies(projCtx.Task) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } task.DependsOn = deps task.TaskWaiting = taskWaiting // Activating and deactivating tasks should clear out the // MinQueuePos but just in case, lets not show it if we shouldn't if projCtx.Task.Status == evergreen.TaskUndispatched && projCtx.Task.Activated { task.MinQueuePos = projCtx.Task.MinQueuePos } if projCtx.Task.HostId != "" { task.HostDNS = projCtx.Task.HostId task.HostId = projCtx.Task.HostId taskHost, err := host.FindOne(host.ById(projCtx.Task.HostId)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if taskHost != nil { task.HostDNS = taskHost.Host } } if projCtx.Patch != nil { taskOnBaseCommit, err := projCtx.Task.FindTaskOnBaseCommit() if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, err) return } taskPatch := &uiPatch{Patch: *projCtx.Patch} if taskOnBaseCommit != nil { taskPatch.BaseTaskId = taskOnBaseCommit.Id } taskPatch.StatusDiffs = model.StatusDiffTasks(taskOnBaseCommit, projCtx.Task).Tests task.PatchInfo = taskPatch } flashes := PopFlashes(uis.CookieStore, r, w) pluginContext := projCtx.ToPluginContext(uis.Settings, GetUser(r)) pluginContent := getPluginDataAndHTML(uis, plugin.TaskPage, pluginContext) uis.WriteHTML(w, http.StatusOK, struct { ProjectData projectContext User *user.DBUser Flashes []interface{} Task uiTaskData PluginContent pluginData }{projCtx, GetUser(r), flashes, task, pluginContent}, "base", "task.html", "base_angular.html", "menu.html") }