func TestBuildMarkStarted(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",
Status: evergreen.BuildCreated,
}
So(b.Insert(), ShouldBeNil)
Convey("marking it as started should update the status and"+
" start time, both in memory and in the database", func() {
startTime := time.Now()
So(build.TryMarkStarted(b.Id, startTime), ShouldBeNil)
// refresh from db and check again
b, err := build.FindOne(build.ById(b.Id))
So(err, ShouldBeNil)
So(b.Status, ShouldEqual, evergreen.BuildStarted)
So(b.StartTime.Round(time.Second).Equal(
startTime.Round(time.Second)), ShouldBeTrue)
})
})
}
// populateTaskBuildVersion takes a task, build, and version ID and populates a projectContext
// with as many of the task, build, and version documents as possible.
// If any of the provided IDs is blank, they will be inferred from the more selective ones.
// Returns the project ID of the data found, which may be blank if the IDs are empty.
func (pc *projectContext) populateTaskBuildVersion(taskId, buildId, versionId string) (string, error) {
projectId := ""
var err error
// Fetch task if there's a task ID present; if we find one, populate build/version IDs from it
if len(taskId) > 0 {
pc.Task, err = task.FindOne(task.ById(taskId))
if err != nil {
return "", err
}
if pc.Task != nil {
// override build and version ID with the ones this task belongs to
buildId = pc.Task.BuildId
versionId = pc.Task.Version
projectId = pc.Task.Project
}
}
// Fetch build if there's a build ID present; if we find one, populate version ID from it
if len(buildId) > 0 {
pc.Build, err = build.FindOne(build.ById(buildId))
if err != nil {
return "", err
}
if pc.Build != nil {
versionId = pc.Build.Version
projectId = pc.Build.Project
}
}
if len(versionId) > 0 {
pc.Version, err = version.FindOne(version.ById(versionId))
if err != nil {
return "", err
}
if pc.Version != nil {
projectId = pc.Version.Identifier
}
}
return projectId, nil
}
// Activates any builds if their BatchTimes have elapsed.
func (repoTracker *RepoTracker) activateElapsedBuilds(v *version.Version) (err error) {
projectId := repoTracker.ProjectRef.Identifier
hasActivated := false
now := time.Now()
for i, status := range v.BuildVariants {
// last comparison is to check that ActivateAt is actually set
if !status.Activated && now.After(status.ActivateAt) && !status.ActivateAt.IsZero() {
evergreen.Logger.Logf(slogger.INFO, "activating variant %v for project %v, revision %v",
status.BuildVariant, projectId, v.Revision)
// Go copies the slice value, we want to modify the actual value
status.Activated = true
status.ActivateAt = now
v.BuildVariants[i] = status
b, err := build.FindOne(build.ById(status.BuildId))
if err != nil {
evergreen.Logger.Logf(slogger.ERROR,
"error retrieving build for project %v, variant %v, build %v: %v",
projectId, status.BuildVariant, status.BuildId, err)
continue
}
evergreen.Logger.Logf(slogger.INFO, "activating build %v for project %v, variant %v",
status.BuildId, projectId, status.BuildVariant)
// Don't need to set the version in here since we do it ourselves in a single update
if err = model.SetBuildActivation(b.Id, true); err != nil {
evergreen.Logger.Logf(slogger.ERROR, "error activating build %v for project %v, variant %v: %v",
b.Id, projectId, status.BuildVariant, err)
continue
}
hasActivated = true
}
}
// If any variants were activated, update the stored version so that we don't
// attempt to activate them again
if hasActivated {
return v.UpdateBuildVariants()
}
return nil
}
func TestMarkAsDispatched(t *testing.T) {
var (
taskId string
hostId string
buildId string
task *Task
myHost *host.Host
b *build.Build
)
Convey("With a task", t, func() {
taskId = "t1"
hostId = "h1"
buildId = "b1"
task = &Task{
Id: taskId,
BuildId: buildId,
}
myHost = &host.Host{
Id: hostId,
}
b = &build.Build{
Id: buildId,
Tasks: []build.TaskCache{
{Id: taskId},
},
}
testutil.HandleTestingErr(
db.ClearCollections(TasksCollection, build.Collection, host.Collection),
t, "Error clearing test collections")
So(task.Insert(), ShouldBeNil)
So(myHost.Insert(), ShouldBeNil)
So(b.Insert(), ShouldBeNil)
Convey("when marking the task as dispatched, the fields for"+
" the task, the host it is on, and the build it is a part of"+
" should be set to reflect this", func() {
// mark the task as dispatched
So(task.MarkAsDispatched(myHost, time.Now()), ShouldBeNil)
// make sure the task's fields were updated, both in memory and
// in the db
So(task.DispatchTime, ShouldNotResemble, time.Unix(0, 0))
So(task.Status, ShouldEqual, evergreen.TaskDispatched)
So(task.HostId, ShouldEqual, myHost.Id)
So(task.LastHeartbeat, ShouldResemble, task.DispatchTime)
task, err := FindTask(taskId)
So(err, ShouldBeNil)
So(task.DispatchTime, ShouldNotResemble, time.Unix(0, 0))
So(task.Status, ShouldEqual, evergreen.TaskDispatched)
So(task.HostId, ShouldEqual, myHost.Id)
So(task.LastHeartbeat, ShouldResemble, task.DispatchTime)
// make sure the build's fields were updated in the db
b, err = build.FindOne(build.ById(buildId))
So(err, ShouldBeNil)
So(b.Tasks[0].Status, ShouldEqual, evergreen.TaskDispatched)
})
})
}
// 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
}
// 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)
//.........这里部分代码省略.........
func TestBuildSetActivated(t *testing.T) {
Convey("With a build", t, func() {
testutil.HandleTestingErr(db.ClearCollections(build.Collection, task.Collection), t,
"Error clearing test collection")
Convey("when changing the activated status of the build to true", func() {
Convey("the activated status of the build and all undispatched"+
" tasks that are part of it should be set", func() {
user := "differentUser"
b := &build.Build{
Id: "build",
Activated: true,
BuildVariant: "bv",
}
So(b.Insert(), ShouldBeNil)
// insert three tasks, with only one of them undispatched and
// belonging to the correct build
wrongBuildId := &task.Task{
Id: "wrongBuildId",
BuildId: "blech",
Status: evergreen.TaskUndispatched,
Activated: true,
}
So(wrongBuildId.Insert(), ShouldBeNil)
wrongStatus := &task.Task{
Id: "wrongStatus",
BuildId: b.Id,
Status: evergreen.TaskDispatched,
Activated: true,
}
So(wrongStatus.Insert(), ShouldBeNil)
matching := &task.Task{
Id: "matching",
BuildId: b.Id,
Status: evergreen.TaskUndispatched,
Activated: true,
}
So(matching.Insert(), ShouldBeNil)
differentUser := &task.Task{
Id: "differentUser",
BuildId: b.Id,
Status: evergreen.TaskUndispatched,
Activated: true,
ActivatedBy: user,
}
So(differentUser.Insert(), ShouldBeNil)
So(SetBuildActivation(b.Id, false, evergreen.DefaultTaskActivator), ShouldBeNil)
// the build should have been updated in the db
b, err := build.FindOne(build.ById(b.Id))
So(err, ShouldBeNil)
So(b.Activated, ShouldBeFalse)
So(b.ActivatedBy, ShouldEqual, evergreen.DefaultTaskActivator)
// only the matching task should have been updated that has not been set by a user
deactivatedTasks, err := task.Find(task.ByActivation(false))
So(err, ShouldBeNil)
So(len(deactivatedTasks), ShouldEqual, 1)
So(deactivatedTasks[0].Id, ShouldEqual, matching.Id)
// task with the different user activating should be activated with that user
differentUserTask, err := task.FindOne(task.ById(differentUser.Id))
So(err, ShouldBeNil)
So(differentUserTask.Activated, ShouldBeTrue)
So(differentUserTask.ActivatedBy, ShouldEqual, user)
})
Convey("all of the undispatched task caches within the build"+
" should be updated, both in memory and in the"+
" database", func() {
b := &build.Build{
Id: "build",
Activated: true,
BuildVariant: "foo",
Tasks: []build.TaskCache{
{
Id: "tc1",
Status: evergreen.TaskUndispatched,
Activated: true,
},
{
Id: "tc2",
Status: evergreen.TaskDispatched,
Activated: true,
},
{
Id: "tc3",
Status: evergreen.TaskUndispatched,
//.........这里部分代码省略.........
请发表评论