func TestDeficitBasedHostAllocator(t *testing.T) { var taskIds []string var runningTaskIds []string var hostIds []string var dist distro.Distro var hostAllocator *DeficitBasedHostAllocator Convey("With a deficit based host allocator,"+ " determining the number of new hosts to spin up...", t, func() { hostAllocator = &DeficitBasedHostAllocator{} taskIds = []string{"t1", "t2", "t3", "t4", "t5"} runningTaskIds = []string{"t1", "t2", "t3", "t4", "t5"} hostIds = []string{"h1", "h2", "h3", "h4", "h5"} dist = distro.Distro{Provider: "ec2"} Convey("if there are no tasks to run, no new hosts should be needed", func() { hosts := []host.Host{ {Id: hostIds[0]}, {Id: hostIds[1]}, {Id: hostIds[2]}, } dist.PoolSize = len(hosts) + 5 hostAllocatorData := &HostAllocatorData{ existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) }) Convey("if the number of existing hosts equals the max hosts, no new"+ " hosts can be spawned", func() { taskQueueItems := []model.TaskQueueItem{ {Id: taskIds[0]}, {Id: taskIds[1]}, {Id: taskIds[2]}, {Id: taskIds[3]}, } dist.PoolSize = 0 hostAllocatorData := &HostAllocatorData{ existingDistroHosts: map[string][]host.Host{}, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) hosts := []host.Host{ {Id: hostIds[0]}, } dist.PoolSize = len(hosts) hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) }) Convey("if the number of existing hosts exceeds the max hosts, no new"+ " hosts can be spawned", func() { taskQueueItems := []model.TaskQueueItem{ {Id: taskIds[0]}, {Id: taskIds[1]}, {Id: taskIds[2]}, {Id: taskIds[3]}, } hosts := []host.Host{ {Id: hostIds[0]}, {Id: hostIds[1]}, } dist.PoolSize = 1 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) }) Convey("if the number of tasks to run is less than the number of free"+ " hosts, no new hosts are needed", func() { taskQueueItems := []model.TaskQueueItem{ {Id: taskIds[0]}, {Id: taskIds[1]}, } hosts := []host.Host{ {Id: hostIds[0]}, {Id: hostIds[1], RunningTask: runningTaskIds[0]}, {Id: hostIds[2]}, {Id: hostIds[3]}, } dist.PoolSize = len(hosts) + 5 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) }) Convey("if the number of tasks to run is equal to the number of free"+ " hosts, no new hosts are needed", func() { taskQueueItems := []model.TaskQueueItem{ {Id: taskIds[0]}, {Id: taskIds[1]}, } hosts := []host.Host{ {Id: hostIds[0]}, {Id: hostIds[1], RunningTask: runningTaskIds[0]}, {Id: hostIds[2], RunningTask: runningTaskIds[1]}, {Id: hostIds[3]}, } dist.PoolSize = len(hosts) + 5 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) }) Convey("if the number of tasks to run exceeds the number of free"+ " hosts, new hosts are needed up to the maximum allowed for the"+ " distro", func() { taskQueueItems := []model.TaskQueueItem{ {Id: taskIds[0]}, {Id: taskIds[1]}, {Id: taskIds[2]}, {Id: taskIds[3]}, {Id: taskIds[4]}, } hosts := []host.Host{ {Id: hostIds[0]}, {Id: hostIds[1], RunningTask: runningTaskIds[0]}, {Id: hostIds[2], RunningTask: runningTaskIds[1]}, {Id: hostIds[3]}, {Id: hostIds[4], RunningTask: runningTaskIds[2]}, } dist.PoolSize = 9 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 3) dist.PoolSize = 8 hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 3) dist.PoolSize = 7 hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 2) dist.PoolSize = 6 hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 1) }) Convey("if the distro cannot be used to spawn hosts, then no new hosts"+ " can be spawned", func() { hosts := []host.Host{ {Id: hostIds[0]}, } taskQueueItems := []model.TaskQueueItem{ {Id: taskIds[0]}, {Id: taskIds[1]}, {Id: taskIds[2]}, } dist.PoolSize = 20 dist.Provider = "static" hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } So(hostAllocator.numNewHostsForDistro(hostAllocatorData, dist, hostAllocatorTestConf), ShouldEqual, 0) }) }) }
func TestDurationBasedHostAllocator(t *testing.T) { var taskIds []string var runningTaskIds []string var hostIds []string var dist distro.Distro var testTaskDuration time.Duration var taskDurations model.ProjectTaskDurations var durationBasedHostAllocator *DurationBasedHostAllocator Convey("With a duration based host allocator,"+ " determining the number of new hosts to spin up", t, func() { durationBasedHostAllocator = &DurationBasedHostAllocator{} taskIds = []string{"t1", "t2", "t3", "t4", "t5"} runningTaskIds = []string{"t1", "t2", "t3", "t4", "t5"} hostIds = []string{"h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", "h9"} dist = distro.Distro{Provider: "ec2"} testTaskDuration = time.Duration(2) * time.Minute taskDurations = model.ProjectTaskDurations{ TaskDurationByProject: map[string]*model.BuildVariantTaskDurations{ "": &model.BuildVariantTaskDurations{ TaskDurationByBuildVariant: map[string]*model.TaskDurations{ "": &model.TaskDurations{ TaskDurationByDisplayName: map[string]time.Duration{ "": testTaskDuration, }, }, }, }, }, } So(db.Clear(task.Collection), ShouldBeNil) Convey("if there are no tasks to run, no new hosts should be needed", func() { hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1]}, host.Host{Id: hostIds[2]}, } dist.PoolSize = len(hosts) + 5 hostAllocatorData := &HostAllocatorData{ existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) }) Convey("if the number of existing hosts equals the max hosts, no new"+ " hosts can be spawned", func() { taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0]}, model.TaskQueueItem{Id: taskIds[1]}, model.TaskQueueItem{Id: taskIds[2]}, model.TaskQueueItem{Id: taskIds[3]}, } dist.PoolSize = 0 hostAllocatorData := &HostAllocatorData{ existingDistroHosts: map[string][]host.Host{}, distros: map[string]distro.Distro{ "": dist, }, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) hosts := []host.Host{ host.Host{Id: hostIds[0]}, } dist.PoolSize = len(hosts) hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } tasksAccountedFor = make(map[string]bool) distroScheduleData = make(map[string]DistroScheduleData) newHosts, err = durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) }) Convey("if the number of existing hosts exceeds the max hosts, no new"+ " hosts can be spawned", func() { taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0]}, model.TaskQueueItem{Id: taskIds[1]}, model.TaskQueueItem{Id: taskIds[2]}, model.TaskQueueItem{Id: taskIds[3]}, } hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1]}, } dist.PoolSize = 1 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) }) Convey("if the number of tasks to run is less than the number of free"+ " hosts, no new hosts are needed", func() { taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0]}, model.TaskQueueItem{Id: taskIds[1]}, } hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1]}, host.Host{Id: hostIds[2]}, } dist.PoolSize = len(hosts) + 5 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) }) Convey("if the number of tasks to run is equal to the number of free"+ " hosts, no new hosts are needed", func() { hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1], RunningTask: runningTaskIds[0]}, host.Host{Id: hostIds[2], RunningTask: runningTaskIds[1]}, host.Host{Id: hostIds[3]}, } taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0]}, model.TaskQueueItem{Id: taskIds[1]}, } dist.PoolSize = len(hosts) + 5 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) // tasks running on hosts for _, runningTaskId := range runningTaskIds { task := task.Task{Id: runningTaskId} So(task.Insert(), ShouldBeNil) } newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) }) Convey("if the number of tasks to run exceeds the number of free"+ " hosts, new hosts are needed up to the maximum allowed for the"+ " dist", func() { expDur := time.Duration(200) * time.Minute // all runnable tasks have an expected duration of expDur (200mins) taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[1], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[2], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[3], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[4], ExpectedDuration: expDur}, } // running tasks have a time to completion of about 1 minute hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1], RunningTask: runningTaskIds[0]}, host.Host{Id: hostIds[2], RunningTask: runningTaskIds[1]}, host.Host{Id: hostIds[3]}, host.Host{Id: hostIds[4], RunningTask: runningTaskIds[2]}, } dist.PoolSize = 9 // In this test: // // 1. Total distro duration is: // (len(taskQueueItems) * expDur ) + // time left on hosts with running tasks // which comes out to: // (5 * 200 * 60) + (60 * 3) ~ 60180 (in seconds) // // 2. MAX_DURATION_PER_DISTRO = 7200 (2 hours) // // 3. We have 5 existing hosts // // Thus, our duration based host allocator will always return 8 - // which is greater than what distro.PoolSize-len(existingDistroHosts) // will ever return in this situation. // // Hence, we should always expect to use that minimum. // hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) // tasks running on hosts for _, runningTaskId := range runningTaskIds { task := task.Task{Id: runningTaskId} So(task.Insert(), ShouldBeNil) } // total running duration here is newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 3) dist.PoolSize = 8 hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor = make(map[string]bool) distroScheduleData = make(map[string]DistroScheduleData) newHosts, err = durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 3) dist.PoolSize = 7 hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor = make(map[string]bool) distroScheduleData = make(map[string]DistroScheduleData) newHosts, err = durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 2) dist.PoolSize = 6 hostAllocatorData = &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor = make(map[string]bool) newHosts, err = durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 1) }) Convey("if the distro cannot be used to spawn hosts, then no new "+ "hosts can be spawned", func() { expDur := time.Duration(200) * time.Minute // all runnable tasks have an expected duration of expDur (200mins) taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[1], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[2], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[3], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[4], ExpectedDuration: expDur}, } // running tasks have a time to completion of about 1 minute hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1]}, host.Host{Id: hostIds[2]}, host.Host{Id: hostIds[3]}, host.Host{Id: hostIds[4]}, } dist.PoolSize = 20 dist.Provider = "static" hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 0) }) Convey("if the duration based estimate is less than the maximum "+ "\nnumber of new hosts allowed for this distro, the estimate of new "+ "\nhosts should be used", func() { expDur := time.Duration(200) * time.Minute // all runnable tasks have an expected duration of expDur (200mins) taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[1], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[2], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[3], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[4], ExpectedDuration: expDur}, } // running tasks have a time to completion of about 1 minute hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1], RunningTask: runningTaskIds[0]}, host.Host{Id: hostIds[2], RunningTask: runningTaskIds[1]}, host.Host{Id: hostIds[3]}, host.Host{Id: hostIds[4], RunningTask: runningTaskIds[2]}, } dist.PoolSize = 20 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) // tasks running on hosts for _, runningTaskId := range runningTaskIds { task := task.Task{Id: runningTaskId} So(task.Insert(), ShouldBeNil) } newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 3) }) Convey("if the duration based estimate is less than the maximum "+ "\nnumber of new hosts allowed for this distro, but greater than "+ "\nthe difference between the number of runnable tasks and the "+ "\nnumber of free hosts, that difference should be used", func() { expDur := time.Duration(400) * time.Minute // all runnable tasks have an expected duration of expDur (200mins) taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[1], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[2], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[3], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[4], ExpectedDuration: expDur}, } // running tasks have a time to completion of about 1 minute hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1], RunningTask: runningTaskIds[0]}, host.Host{Id: hostIds[2], RunningTask: runningTaskIds[1]}, host.Host{Id: hostIds[3]}, host.Host{Id: hostIds[4], RunningTask: runningTaskIds[2]}, } dist.PoolSize = 20 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) // tasks running on hosts for _, runningTaskId := range runningTaskIds { task := task.Task{Id: runningTaskId} So(task.Insert(), ShouldBeNil) } // estimates based on data // duration estimate: 11 // max new hosts allowed: 15 // 'one-host-per-scheduled-task': 3 newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 3) }) Convey("if the duration based estimate is less than both the maximum "+ "\nnumber of new hosts allowed for this distro, and the "+ "\ndifference between the number of runnable tasks and the "+ "\nnumber of free hosts, then the duration based estimate should "+ "be used", func() { expDur := time.Duration(180) * time.Minute // all runnable tasks have an expected duration of expDur (200mins) taskQueueItems := []model.TaskQueueItem{ model.TaskQueueItem{Id: taskIds[0], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[1], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[2], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[3], ExpectedDuration: expDur}, model.TaskQueueItem{Id: taskIds[4], ExpectedDuration: expDur}, } // running tasks have a time to completion of about 1 minute hosts := []host.Host{ host.Host{Id: hostIds[0]}, host.Host{Id: hostIds[1], RunningTask: runningTaskIds[0]}, host.Host{Id: hostIds[2], RunningTask: runningTaskIds[1]}, host.Host{Id: hostIds[3]}, host.Host{Id: hostIds[4], RunningTask: runningTaskIds[2]}, host.Host{Id: hostIds[5]}, } dist.PoolSize = 20 hostAllocatorData := &HostAllocatorData{ taskQueueItems: map[string][]model.TaskQueueItem{ "": taskQueueItems, }, existingDistroHosts: map[string][]host.Host{ "": hosts, }, distros: map[string]distro.Distro{ "": dist, }, projectTaskDurations: taskDurations, } tasksAccountedFor := make(map[string]bool) distroScheduleData := make(map[string]DistroScheduleData) // tasks running on hosts for _, runningTaskId := range runningTaskIds { task := task.Task{Id: runningTaskId} So(task.Insert(), ShouldBeNil) } // estimates based on data // duration estimate: 2 // max new hosts allowed: 15 // 'one-host-per-scheduled-task': 3 newHosts, err := durationBasedHostAllocator. numNewHostsForDistro(hostAllocatorData, dist, tasksAccountedFor, distroScheduleData, hostAllocatorTestConf) So(err, ShouldBeNil) So(newHosts, ShouldEqual, 2) }) }) }