func (TestSuite) TestFindJobToStop(c *C) { s := &Scheduler{ isLeader: typeconv.BoolPtr(true), jobs: make(Jobs), logger: log15.New(), } // populate s.jobs with jobs in various states, oldest first formation := &Formation{} typ := "web" start := time.Now() startedAt := func(i int) time.Time { return start.Add(time.Duration(int64(i)) * time.Second) } for i, state := range []JobState{ JobStatePending, JobStateStarting, JobStateRunning, JobStatePending, JobStateStarting, JobStateRunning, JobStateStopping, JobStateStopped, JobStatePending, JobStateStarting, JobStateRunning, } { id := fmt.Sprintf("job%d", i) s.jobs[id] = &Job{ ID: id, Formation: formation, Type: typ, StartedAt: startedAt(i), State: state, } } nextJob := func() *Job { job, err := s.findJobToStop(formation, typ) c.Assert(err, IsNil) delete(s.jobs, job.ID) return job } // expect the three pending jobs first c.Assert(nextJob().State, Equals, JobStatePending) c.Assert(nextJob().State, Equals, JobStatePending) c.Assert(nextJob().State, Equals, JobStatePending) // then the starting jobs, newest first c.Assert(nextJob().ID, Equals, "job9") c.Assert(nextJob().ID, Equals, "job4") c.Assert(nextJob().ID, Equals, "job1") // then the running jobs, newest first c.Assert(nextJob().ID, Equals, "job10") c.Assert(nextJob().ID, Equals, "job5") c.Assert(nextJob().ID, Equals, "job2") }
func (TestSuite) TestJobPlacementTags(c *C) { // create a scheduler with tagged hosts s := &Scheduler{ isLeader: typeconv.BoolPtr(true), jobs: make(Jobs), hosts: map[string]*Host{ "host1": {ID: "host1", Tags: map[string]string{"disk": "mag", "cpu": "fast"}}, "host2": {ID: "host2", Tags: map[string]string{"disk": "ssd", "cpu": "slow"}}, "host3": {ID: "host3", Tags: map[string]string{"disk": "ssd", "cpu": "fast"}}, }, logger: log15.New(), } // use a formation with tagged process types formation := NewFormation(&ct.ExpandedFormation{ App: &ct.App{ID: "app"}, Release: &ct.Release{ID: "release", Processes: map[string]ct.ProcessType{ "web": {}, "db": {}, "worker": {}, "clock": {}, }}, ImageArtifact: &ct.Artifact{}, Tags: map[string]map[string]string{ "web": nil, "db": {"disk": "ssd"}, "worker": {"cpu": "fast"}, "clock": {"disk": "ssd", "cpu": "slow"}, }, }) // continually place jobs, and check they get placed in a round-robin // fashion on the hosts matching the type's tags type test struct { typ string host string } for i, t := range []*test{ // web go on all hosts {typ: "web", host: "host1"}, {typ: "web", host: "host2"}, {typ: "web", host: "host3"}, {typ: "web", host: "host1"}, {typ: "web", host: "host2"}, {typ: "web", host: "host3"}, // db go on hosts 2 and 3 {typ: "db", host: "host2"}, {typ: "db", host: "host3"}, {typ: "db", host: "host2"}, {typ: "db", host: "host3"}, // worker go on hosts 1 and 3 {typ: "worker", host: "host1"}, {typ: "worker", host: "host3"}, {typ: "worker", host: "host1"}, {typ: "worker", host: "host3"}, // clock go on host 2 {typ: "clock", host: "host2"}, {typ: "clock", host: "host2"}, {typ: "clock", host: "host2"}, } { job := s.jobs.Add(&Job{ID: fmt.Sprintf("job%d", i), Formation: formation, Type: t.typ, state: JobStatePending}) req := &PlacementRequest{Job: job, Err: make(chan error, 1)} s.HandlePlacementRequest(req) c.Assert(<-req.Err, IsNil, Commentf("placing job %d", i)) c.Assert(req.Host.ID, Equals, t.host, Commentf("placing job %d", i)) } }