// RefreshTasksCache updates a build document so that the tasks cache reflects the correct current // state of the tasks it represents. func RefreshTasksCache(buildId string) error { tasks, err := task.Find(task.ByBuildId(buildId).WithFields(task.IdKey, task.DisplayNameKey, task.StatusKey, task.DetailsKey, task.StartTimeKey, task.TimeTakenKey, task.ActivatedKey, task.DependsOnKey)) if err != nil { return err } cache := CreateTasksCache(tasks) return build.SetTasksCache(buildId, cache) }
// updateMakespans func updateMakespans(b *build.Build) error { // find all tasks associated with the build tasks, err := task.Find(task.ByBuildId(b.Id)) if err != nil { return err } depPath := FindPredictedMakespan(tasks) return b.UpdateMakespans(depPath.TotalTime, CalculateActualMakespan(tasks)) }
func TestDeletingBuild(t *testing.T) { Convey("With a build", t, func() { testutil.HandleTestingErr(db.Clear(build.Collection), t, "Error clearing"+ " '%v' collection", build.Collection) b := &build.Build{ Id: "build", } So(b.Insert(), ShouldBeNil) Convey("deleting it should remove it and all its associated"+ " tasks from the database", func() { testutil.HandleTestingErr(db.ClearCollections(task.Collection), t, "Error"+ " clearing '%v' collection", task.Collection) // insert two tasks that are part of the build, and one that isn't matchingTaskOne := &task.Task{ Id: "matchingOne", BuildId: b.Id, } So(matchingTaskOne.Insert(), ShouldBeNil) matchingTaskTwo := &task.Task{ Id: "matchingTwo", BuildId: b.Id, } So(matchingTaskTwo.Insert(), ShouldBeNil) nonMatchingTask := &task.Task{ Id: "nonMatching", BuildId: "blech", } So(nonMatchingTask.Insert(), ShouldBeNil) // delete the build, make sure only it and its tasks are deleted So(DeleteBuild(b.Id), ShouldBeNil) b, err := build.FindOne(build.ById(b.Id)) So(err, ShouldBeNil) So(b, ShouldBeNil) matchingTasks, err := task.Find(task.ByBuildId("build")) So(err, ShouldBeNil) So(len(matchingTasks), ShouldEqual, 0) nonMatchingTask, err = task.FindOne(task.ById(nonMatchingTask.Id)) So(err, ShouldBeNil) So(nonMatchingTask, ShouldNotBeNil) }) }) }
// getUiTaskCache takes a build object and returns a slice of // uiTask objects suitable for front-end func getUiTaskCache(build *build.Build) ([]uiTask, error) { tasks, err := task.Find(task.ByBuildId(build.Id)) if err != nil { return nil, err } idToTask := make(map[string]task.Task) for _, task := range tasks { idToTask[task.Id] = task } // Insert the tasks in the same order as the task cache uiTasks := make([]uiTask, 0, len(build.Tasks)) for _, taskCache := range build.Tasks { taskAsUI := uiTask{Task: idToTask[taskCache.Id]} uiTasks = append(uiTasks, taskAsUI) } return uiTasks, nil }
// Given a patch version and set of variant/task pairs, creates any tasks that don't exist yet, // within the set of already existing builds. func AddNewTasksForPatch(p *patch.Patch, patchVersion *version.Version, project *Project, pairs TVPairSet) error { builds, err := build.Find(build.ByIds(patchVersion.BuildIds).WithFields(build.IdKey, build.BuildVariantKey)) if err != nil { return err } for _, b := range builds { // Find the set of task names that already exist for the given build tasksInBuild, err := task.Find(task.ByBuildId(b.Id).WithFields(task.DisplayNameKey)) if err != nil { return err } // build an index to keep track of which tasks already exist existingTasksIndex := map[string]bool{} for _, t := range tasksInBuild { existingTasksIndex[t.DisplayName] = true } // if the patch is activated, treat the build as activated b.Activated = p.Activated // build a list of tasks that haven't been created yet for the given variant, but have // a record in the TVPairSet indicating that it should exist tasksToAdd := []string{} for _, taskname := range pairs.TaskNames(b.BuildVariant) { if _, ok := existingTasksIndex[taskname]; ok { continue } tasksToAdd = append(tasksToAdd, taskname) } if len(tasksToAdd) == 0 { // no tasks to add, so we do nothing. continue } // Add the new set of tasks to the build. if _, err = AddTasksToBuild(&b, project, patchVersion, tasksToAdd); err != nil { return err } } return nil }
func TestBuildSetPriority(t *testing.T) { Convey("With a build", t, func() { testutil.HandleTestingErr(db.ClearCollections(build.Collection, task.Collection), t, "Error clearing test collection") b := &build.Build{ Id: "build", } So(b.Insert(), ShouldBeNil) taskOne := &task.Task{Id: "taskOne", BuildId: b.Id} So(taskOne.Insert(), ShouldBeNil) taskTwo := &task.Task{Id: "taskTwo", BuildId: b.Id} So(taskTwo.Insert(), ShouldBeNil) taskThree := &task.Task{Id: "taskThree", BuildId: b.Id} So(taskThree.Insert(), ShouldBeNil) Convey("setting its priority should update the priority"+ " of all its tasks in the database", func() { So(SetBuildPriority(b.Id, 42), ShouldBeNil) tasks, err := task.Find(task.ByBuildId(b.Id)) So(err, ShouldBeNil) So(len(tasks), ShouldEqual, 3) So(tasks[0].Priority, ShouldEqual, 42) So(tasks[1].Priority, ShouldEqual, 42) So(tasks[2].Priority, ShouldEqual, 42) }) }) }
// UpdateBuildStatusForTask finds all the builds for a task and updates the // status of the build based on the task's status. func UpdateBuildAndVersionStatusForTask(taskId string) error { // retrieve the task by the task id t, err := task.FindOne(task.ById(taskId)) if err != nil { return err } finishTime := time.Now() // get all of the tasks in the same build b, err := build.FindOne(build.ById(t.BuildId)) if err != nil { return err } buildTasks, err := task.Find(task.ByBuildId(b.Id)) if err != nil { return err } pushTaskExists := false for _, t := range buildTasks { if t.DisplayName == evergreen.PushStage { pushTaskExists = true } } failedTask := false pushSuccess := true pushCompleted := false finishedTasks := 0 // update the build's status based on tasks for this build for _, t := range buildTasks { if task.IsFinished(t) { finishedTasks += 1 // if it was a compile task, mark the build status accordingly if t.DisplayName == evergreen.CompileStage { if t.Status != evergreen.TaskSucceeded { failedTask = true finishedTasks = -1 err = b.MarkFinished(evergreen.BuildFailed, finishTime) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking build as finished: %v", err) return err } break } } else if t.DisplayName == evergreen.PushStage { pushCompleted = true // if it's a finished push, check if it was successful if t.Status != evergreen.TaskSucceeded { err = b.UpdateStatus(evergreen.BuildFailed) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error updating build status: %v", err) return err } pushSuccess = false } } else { // update the build's status when a test task isn't successful if t.Status != evergreen.TaskSucceeded { err = b.UpdateStatus(evergreen.BuildFailed) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error updating build status: %v", err) return err } failedTask = true } } } } // if there are no failed tasks, mark the build as started if !failedTask { err = b.UpdateStatus(evergreen.BuildStarted) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error updating build status: %v", err) return err } } // if a compile task didn't fail, then the // build is only finished when both the compile // and test tasks are completed or when those are // both completed in addition to a push (a push // does not occur if there's a failed task) if finishedTasks >= len(buildTasks)-1 { if !failedTask { if pushTaskExists { // this build has a push task associated with it. if pushCompleted && pushSuccess { // the push succeeded, so mark the build as succeeded. err = b.MarkFinished(evergreen.BuildSucceeded, finishTime) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking build as finished: %v", err) return err } } else if pushCompleted && !pushSuccess { // the push failed, mark build failed. err = b.MarkFinished(evergreen.BuildFailed, finishTime) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking build as finished: %v", err) return err } } else { //This build does have a "push" task, but it hasn't finished yet //So do nothing, since we don't know the status yet. } if err = MarkVersionCompleted(b.Version, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking version as finished: %v", err) return err } } else { // this build has no push task. so go ahead and mark it success/failure. if err = b.MarkFinished(evergreen.BuildSucceeded, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking build as finished: %v", err) return err } if b.Requester == evergreen.PatchVersionRequester { if err = TryMarkPatchBuildFinished(b, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking patch as finished: %v", err) return err } } if err = MarkVersionCompleted(b.Version, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking version as finished: %v", err) return err } } } else { // some task failed if err = b.MarkFinished(evergreen.BuildFailed, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking build as finished: %v", err) return err } if b.Requester == evergreen.PatchVersionRequester { if err = TryMarkPatchBuildFinished(b, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking patch as finished: %v", err) return err } } if err = MarkVersionCompleted(b.Version, finishTime); err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error marking version as finished: %v", err) return err } } // update the build's makespan information if the task has finished err = updateMakespans(b) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error updating makespan information: %v", err) return err } } // this is helpful for when we restart a compile task if finishedTasks == 0 { err = b.UpdateStatus(evergreen.BuildCreated) if err != nil { evergreen.Logger.Errorf(slogger.ERROR, "Error updating build status: %v", err) return err } } return nil }