2015-08-30 03:07:24 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2016-01-21 22:09:04 +00:00
|
|
|
"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)
|
|
|
|
}
|
|
|
|
|
2016-02-02 19:09:29 +00:00
|
|
|
type MockTaskStateUpdater struct {
|
|
|
|
state string
|
|
|
|
events []*structs.TaskEvent
|
|
|
|
}
|
2015-08-30 03:07:24 +00:00
|
|
|
|
2016-02-02 19:09:29 +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
|
|
|
|
2015-11-14 06:07:13 +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]
|
2015-12-11 19:02:23 +00:00
|
|
|
consulClient, _ := NewConsulService(&consulServiceConfig{logger, "127.0.0.1:8500", "", "", false, false, &structs.Node{}})
|
2015-09-24 23:00:36 +00:00
|
|
|
// 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-24 23:00:36 +00:00
|
|
|
|
2015-09-25 23:49:14 +00:00
|
|
|
allocDir := allocdir.NewAllocDir(filepath.Join(conf.AllocDir, alloc.ID))
|
|
|
|
allocDir.Build([]*structs.Task{task})
|
|
|
|
|
2015-11-06 02:31:39 +00:00
|
|
|
ctx := driver.NewExecContext(allocDir, alloc.ID)
|
2015-11-03 17:24:26 +00:00
|
|
|
rp := structs.NewRestartPolicy(structs.JobTypeService)
|
2015-12-16 22:39:37 +00:00
|
|
|
restartTracker := newRestartTracker(rp)
|
2015-11-14 06:07:13 +00:00
|
|
|
if !restarts {
|
|
|
|
restartTracker = noRestartsTracker()
|
|
|
|
}
|
|
|
|
|
2016-02-02 19:09:29 +00:00
|
|
|
tr := NewTaskRunner(logger, conf, upd.Update, ctx, mock.Alloc(), task, restartTracker, consulClient)
|
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)
|
2016-02-02 19:09:29 +00:00
|
|
|
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-02 19:09:29 +00:00
|
|
|
if len(upd.events) != 2 {
|
|
|
|
t.Fatalf("should have 2 updates: %#v", upd.events)
|
2015-08-30 03:07:24 +00:00
|
|
|
}
|
|
|
|
|
2016-02-02 19:09:29 +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
|
|
|
}
|
2015-11-14 06:07:13 +00:00
|
|
|
|
2016-02-02 19:09:29 +00:00
|
|
|
if upd.events[0].Type != structs.TaskStarted {
|
|
|
|
t.Fatalf("First Event was %v; want %v", upd.events[0].Type, structs.TaskStarted)
|
2015-08-30 03:07:24 +00:00
|
|
|
}
|
2015-11-14 06:07:13 +00:00
|
|
|
|
2016-02-02 19:09:29 +00:00
|
|
|
if upd.events[1].Type != structs.TaskTerminated {
|
|
|
|
t.Fatalf("First Event was %v; want %v", upd.events[1].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)
|
2016-02-02 19:09:29 +00:00
|
|
|
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"
|
2015-11-18 23:16:42 +00:00
|
|
|
tr.task.Config["args"] = []string{"10"}
|
2015-08-30 03:07:24 +00:00
|
|
|
go tr.Run()
|
|
|
|
|
|
|
|
// Begin the tear down
|
|
|
|
go func() {
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
tr.Destroy()
|
|
|
|
}()
|
|
|
|
|
|
|
|
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-02 19:09:29 +00:00
|
|
|
if len(upd.events) != 2 {
|
|
|
|
t.Fatalf("should have 2 updates: %#v", upd.events)
|
2015-08-30 03:07:24 +00:00
|
|
|
}
|
2015-11-14 06:07:13 +00:00
|
|
|
|
2016-02-02 19:09:29 +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
|
|
|
}
|
2015-11-14 06:07:13 +00:00
|
|
|
|
2016-02-02 19:09:29 +00:00
|
|
|
if upd.events[0].Type != structs.TaskStarted {
|
|
|
|
t.Fatalf("First Event was %v; want %v", upd.events[0].Type, structs.TaskStarted)
|
2015-08-30 03:07:24 +00:00
|
|
|
}
|
2015-11-14 06:07:13 +00:00
|
|
|
|
2016-02-02 19:09:29 +00:00
|
|
|
if upd.events[1].Type != structs.TaskKilled {
|
|
|
|
t.Fatalf("First Event was %v; want %v", upd.events[1].Type, structs.TaskKilled)
|
2015-08-30 03:07:24 +00:00
|
|
|
}
|
2015-11-14 06:07:13 +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)
|
2015-11-14 06:07:13 +00:00
|
|
|
_, 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"
|
2015-11-18 23:16:42 +00:00
|
|
|
tr.task.Config["args"] = []string{"10"}
|
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
|
|
|
|
newTask := new(structs.Task)
|
|
|
|
*newTask = *tr.task
|
|
|
|
newTask.Driver = "foobar"
|
|
|
|
tr.Update(newTask)
|
|
|
|
|
|
|
|
// Wait for update to take place
|
|
|
|
testutil.WaitForResult(func() (bool, error) {
|
|
|
|
return tr.task == newTask, nil
|
|
|
|
}, func(err error) {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTaskRunner_SaveRestoreState(t *testing.T) {
|
2015-11-11 00:53:59 +00:00
|
|
|
ctestutil.ExecCompatible(t)
|
2015-11-14 06:07:13 +00:00
|
|
|
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"
|
2015-11-18 23:16:42 +00:00
|
|
|
tr.task.Config["args"] = []string{"10"}
|
2015-08-30 03:07:24 +00:00
|
|
|
go tr.Run()
|
|
|
|
defer tr.Destroy()
|
|
|
|
|
|
|
|
// Snapshot state
|
2016-01-20 20:00:20 +00:00
|
|
|
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
|
2015-12-11 19:02:23 +00:00
|
|
|
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,
|
2016-02-02 19:09:29 +00:00
|
|
|
tr.ctx, tr.alloc, &structs.Task{Name: tr.task.Name}, tr.restartTracker,
|
2015-11-18 08:50:45 +00:00
|
|
|
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
|
2016-01-21 22:09:04 +00:00
|
|
|
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
|
|
|
}
|