open-nomad/client/task_runner_test.go

230 lines
6.0 KiB
Go
Raw Normal View History

2015-08-30 03:07:24 +00:00
package client
import (
"fmt"
2015-08-30 03:07:24 +00:00
"log"
"os"
2015-09-25 23:49:14 +00:00
"path/filepath"
2015-08-30 03:07:24 +00:00
"testing"
"time"
2015-09-25 23:49:14 +00:00
"github.com/hashicorp/nomad/client/allocdir"
2015-08-30 03:07:24 +00:00
"github.com/hashicorp/nomad/client/driver"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
2015-09-23 00:10:03 +00:00
2015-09-23 01:48:42 +00:00
ctestutil "github.com/hashicorp/nomad/client/testutil"
2015-08-30 03:07:24 +00:00
)
func testLogger() *log.Logger {
return log.New(os.Stderr, "", log.LstdFlags)
}
type MockTaskStateUpdater struct {
state string
events []*structs.TaskEvent
}
2015-08-30 03:07:24 +00:00
func (m *MockTaskStateUpdater) Update(name, state string, event *structs.TaskEvent) {
m.state = state
m.events = append(m.events, event)
}
2015-08-30 03:07:24 +00:00
func testTaskRunner(restarts bool) (*MockTaskStateUpdater, *TaskRunner) {
2015-08-30 03:07:24 +00:00
logger := testLogger()
conf := DefaultConfig()
2015-09-24 21:29:53 +00:00
conf.StateDir = os.TempDir()
conf.AllocDir = os.TempDir()
2015-08-30 03:07:24 +00:00
upd := &MockTaskStateUpdater{}
alloc := mock.Alloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
consulClient, _ := NewConsulService(&consulServiceConfig{logger, "127.0.0.1:8500", "", "", false, false, &structs.Node{}})
// Initialize the port listing. This should be done by the offer process but
// we have a mock so that doesn't happen.
2015-11-15 06:16:32 +00:00
task.Resources.Networks[0].ReservedPorts = []structs.Port{{"", 80}}
2015-09-25 23:49:14 +00:00
allocDir := allocdir.NewAllocDir(filepath.Join(conf.AllocDir, alloc.ID))
allocDir.Build([]*structs.Task{task})
ctx := driver.NewExecContext(allocDir, alloc.ID)
tr := NewTaskRunner(logger, conf, upd.Update, ctx, mock.Alloc(), task, consulClient)
if !restarts {
tr.restartTracker = noRestartsTracker()
}
2015-08-30 03:07:24 +00:00
return upd, tr
}
func TestTaskRunner_SimpleRun(t *testing.T) {
2015-09-23 01:48:42 +00:00
ctestutil.ExecCompatible(t)
upd, tr := testTaskRunner(false)
2015-08-30 03:07:24 +00:00
go tr.Run()
defer tr.Destroy()
2015-09-24 23:59:09 +00:00
defer tr.ctx.AllocDir.Destroy()
2015-08-30 03:07:24 +00:00
select {
case <-tr.WaitCh():
2016-01-21 23:24:24 +00:00
case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
2015-08-30 03:07:24 +00:00
t.Fatalf("timeout")
}
2016-02-19 22:49:43 +00:00
if len(upd.events) != 3 {
t.Fatalf("should have 3 updates: %#v", upd.events)
2015-08-30 03:07:24 +00:00
}
if upd.state != structs.TaskStateDead {
t.Fatalf("TaskState %v; want %v", upd.state, structs.TaskStateDead)
2015-08-30 03:07:24 +00:00
}
2016-02-19 22:49:43 +00:00
if upd.events[0].Type != structs.TaskReceived {
t.Fatalf("First Event was %v; want %v", upd.events[0].Type, structs.TaskReceived)
2015-08-30 03:07:24 +00:00
}
2016-02-19 22:49:43 +00:00
if upd.events[1].Type != structs.TaskStarted {
t.Fatalf("Second Event was %v; want %v", upd.events[1].Type, structs.TaskStarted)
}
if upd.events[2].Type != structs.TaskTerminated {
t.Fatalf("Third Event was %v; want %v", upd.events[2].Type, structs.TaskTerminated)
2015-08-30 03:07:24 +00:00
}
}
func TestTaskRunner_Destroy(t *testing.T) {
2015-09-23 01:48:42 +00:00
ctestutil.ExecCompatible(t)
upd, tr := testTaskRunner(true)
2015-09-24 23:59:09 +00:00
defer tr.ctx.AllocDir.Destroy()
2015-08-30 03:07:24 +00:00
// Change command to ensure we run for a bit
tr.task.Config["command"] = "/bin/sleep"
tr.task.Config["args"] = []string{"1000"}
2015-08-30 03:07:24 +00:00
go tr.Run()
testutil.WaitForResult(func() (bool, error) {
if l := len(upd.events); l != 2 {
return false, fmt.Errorf("Expect two events; got %v", l)
}
if upd.events[0].Type != structs.TaskReceived {
return false, fmt.Errorf("First Event was %v; want %v", upd.events[0].Type, structs.TaskReceived)
}
if upd.events[1].Type != structs.TaskStarted {
return false, fmt.Errorf("Second Event was %v; want %v", upd.events[1].Type, structs.TaskStarted)
}
return true, nil
}, func(err error) {
t.Fatalf("err: %v", err)
})
2015-08-30 03:07:24 +00:00
// Begin the tear down
tr.Destroy()
2015-08-30 03:07:24 +00:00
select {
case <-tr.WaitCh():
2016-01-21 20:55:35 +00:00
case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second):
2015-08-30 03:07:24 +00:00
t.Fatalf("timeout")
}
2016-02-19 22:49:43 +00:00
if len(upd.events) != 3 {
t.Fatalf("should have 3 updates: %#v", upd.events)
2015-08-30 03:07:24 +00:00
}
if upd.state != structs.TaskStateDead {
t.Fatalf("TaskState %v; want %v", upd.state, structs.TaskStateDead)
2015-08-30 03:07:24 +00:00
}
2016-02-19 22:49:43 +00:00
if upd.events[2].Type != structs.TaskKilled {
t.Fatalf("Third Event was %v; want %v", upd.events[2].Type, structs.TaskKilled)
2015-08-30 03:07:24 +00:00
}
2015-08-30 03:07:24 +00:00
}
func TestTaskRunner_Update(t *testing.T) {
2015-09-23 01:48:42 +00:00
ctestutil.ExecCompatible(t)
_, tr := testTaskRunner(false)
2015-08-30 03:07:24 +00:00
// Change command to ensure we run for a bit
tr.task.Config["command"] = "/bin/sleep"
2016-02-04 03:58:39 +00:00
tr.task.Config["args"] = []string{"100"}
2015-08-30 03:07:24 +00:00
go tr.Run()
defer tr.Destroy()
2015-09-24 23:59:09 +00:00
defer tr.ctx.AllocDir.Destroy()
2015-08-30 03:07:24 +00:00
// Update the task definition
updateAlloc := tr.alloc.Copy()
// Update the restart policy
newTG := updateAlloc.Job.TaskGroups[0]
newMode := "foo"
newTG.RestartPolicy.Mode = newMode
newTask := updateAlloc.Job.TaskGroups[0].Tasks[0]
2015-08-30 03:07:24 +00:00
newTask.Driver = "foobar"
// Update the kill timeout
2016-02-04 03:58:39 +00:00
testutil.WaitForResult(func() (bool, error) {
if tr.handle == nil {
return false, fmt.Errorf("task not started")
}
return true, nil
}, func(err error) {
t.Fatalf("err: %v", err)
})
oldHandle := tr.handle.ID()
newTask.KillTimeout = time.Hour
tr.Update(updateAlloc)
2015-08-30 03:07:24 +00:00
// Wait for update to take place
testutil.WaitForResult(func() (bool, error) {
if tr.task != newTask {
return false, fmt.Errorf("task not updated")
}
if tr.restartTracker.policy.Mode != newMode {
return false, fmt.Errorf("restart policy not updated")
}
if tr.handle.ID() == oldHandle {
return false, fmt.Errorf("handle not updated")
}
return true, nil
2015-08-30 03:07:24 +00:00
}, func(err error) {
t.Fatalf("err: %v", err)
})
}
func TestTaskRunner_SaveRestoreState(t *testing.T) {
ctestutil.ExecCompatible(t)
upd, tr := testTaskRunner(false)
2015-08-30 03:07:24 +00:00
// Change command to ensure we run for a bit
tr.task.Config["command"] = "/bin/sleep"
tr.task.Config["args"] = []string{"10"}
2015-08-30 03:07:24 +00:00
go tr.Run()
defer tr.Destroy()
// Snapshot state
time.Sleep(2 * time.Second)
2015-11-11 00:49:38 +00:00
if err := tr.SaveState(); err != nil {
2015-08-30 03:07:24 +00:00
t.Fatalf("err: %v", err)
}
// Create a new task runner
consulClient, _ := NewConsulService(&consulServiceConfig{tr.logger, "127.0.0.1:8500", "", "", false, false, &structs.Node{}})
2015-08-30 03:07:24 +00:00
tr2 := NewTaskRunner(tr.logger, tr.config, upd.Update,
tr.ctx, tr.alloc, &structs.Task{Name: tr.task.Name}, consulClient)
2015-11-11 00:49:38 +00:00
if err := tr2.RestoreState(); err != nil {
2015-08-30 03:07:24 +00:00
t.Fatalf("err: %v", err)
}
go tr2.Run()
2015-11-11 00:49:38 +00:00
defer tr2.Destroy()
2015-08-30 03:07:24 +00:00
// Destroy and wait
testutil.WaitForResult(func() (bool, error) {
return tr2.handle != nil, fmt.Errorf("RestoreState() didn't open handle")
}, func(err error) {
t.Fatalf("err: %v", err)
})
2015-08-30 03:07:24 +00:00
}