// 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 = task.FindOne(task.ById(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 = task.FindOneOld(task.ById(oldTaskId)) 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 = []task.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 := task.FindOneOld(task.ById(oldTaskId)) if err != nil { uis.LoggedError(w, r, http.StatusInternalServerError, err) return } archived = true if taskFromDb == nil { if execution != projCtx.Task.Execution { uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finding old task: %v", err)) return } } else { projCtx.Task = taskFromDb } } // Build a struct containing the subset of task data needed for display in the UI tId := projCtx.Task.Id if archived { tId = projCtx.Task.OldTaskId } task := uiTaskData{ Id: tId, 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.BaseTimeTaken = taskOnBaseCommit.TimeTaken } 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") }
func TestTryResetTask(t *testing.T) { Convey("With a task, a build, version and a project", t, func() { Convey("resetting a task without a max number of executions", func() { testutil.HandleTestingErr(db.ClearCollections(task.Collection, task.OldCollection, build.Collection, version.Collection), t, "Error clearing task and build collections") displayName := "testName" userName := "******" b := &build.Build{ Id: "buildtest", Status: evergreen.BuildStarted, Version: "abc", } v := &version.Version{ Id: b.Version, Status: evergreen.VersionStarted, } testTask := task.Task{ Id: "testone", DisplayName: displayName, Activated: false, BuildId: b.Id, Execution: 1, Project: "sample", Status: evergreen.TaskSucceeded, } p := &Project{ Identifier: "sample", } detail := &apimodels.TaskEndDetail{ Status: evergreen.TaskFailed, } b.Tasks = []build.TaskCache{ { Id: testTask.Id, }, } So(b.Insert(), ShouldBeNil) So(testTask.Insert(), ShouldBeNil) So(v.Insert(), ShouldBeNil) Convey("should reset and add a task to the old tasks collection", func() { So(TryResetTask(testTask.Id, userName, "", p, detail), ShouldBeNil) testTask, err := task.FindOne(task.ById(testTask.Id)) So(err, ShouldBeNil) So(testTask.Details, ShouldResemble, apimodels.TaskEndDetail{}) So(testTask.Status, ShouldEqual, evergreen.TaskUndispatched) So(testTask.FinishTime, ShouldResemble, util.ZeroTime) oldTaskId := fmt.Sprintf("%v_%v", testTask.Id, 1) fmt.Println(oldTaskId) oldTask, err := task.FindOneOld(task.ById(oldTaskId)) So(err, ShouldBeNil) So(oldTask, ShouldNotBeNil) So(oldTask.Execution, ShouldEqual, 1) So(oldTask.Details, ShouldResemble, *detail) So(oldTask.FinishTime, ShouldNotResemble, util.ZeroTime) }) }) Convey("resetting a task with a max number of excutions", func() { testutil.HandleTestingErr(db.ClearCollections(task.Collection, build.Collection, version.Collection), t, "Error clearing task and build collections") displayName := "testName" userName := "******" b := &build.Build{ Id: "buildtest", Status: evergreen.BuildStarted, Version: "abc", } v := &version.Version{ Id: b.Version, Status: evergreen.VersionStarted, } testTask := task.Task{ Id: "testone", DisplayName: displayName, Activated: false, BuildId: b.Id, Execution: evergreen.MaxTaskExecution, Project: "sample", Status: evergreen.TaskSucceeded, } p := &Project{ Identifier: "sample", } detail := &apimodels.TaskEndDetail{ Status: evergreen.TaskFailed, } anotherTask := task.Task{ Id: "two", DisplayName: displayName, Activated: false, BuildId: b.Id, Execution: evergreen.MaxTaskExecution, Project: "sample", Status: evergreen.TaskSucceeded, } b.Tasks = []build.TaskCache{ { Id: testTask.Id, }, { Id: anotherTask.Id, }, } So(b.Insert(), ShouldBeNil) So(testTask.Insert(), ShouldBeNil) So(v.Insert(), ShouldBeNil) So(anotherTask.Insert(), ShouldBeNil) Convey("should not reset if an origin other than the ui package tries to reset", func() { So(TryResetTask(testTask.Id, userName, "", p, detail), ShouldBeNil) testTask, err := task.FindOne(task.ById(testTask.Id)) So(err, ShouldBeNil) So(testTask.Details, ShouldResemble, *detail) So(testTask.Status, ShouldEqual, detail.Status) So(testTask.FinishTime, ShouldNotResemble, util.ZeroTime) }) Convey("should reset and use detail information if the UI package passes in a detail ", func() { So(TryResetTask(anotherTask.Id, userName, evergreen.UIPackage, p, detail), ShouldBeNil) a, err := task.FindOne(task.ById(anotherTask.Id)) So(err, ShouldBeNil) So(a.Details, ShouldResemble, apimodels.TaskEndDetail{}) So(a.Status, ShouldEqual, evergreen.TaskUndispatched) So(a.FinishTime, ShouldResemble, util.ZeroTime) }) }) }) }