github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/client/task_runner_test.go (about)

     1  package client
     2  
     3  import (
     4  	"log"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/hashicorp/nomad/client/allocdir"
    12  	"github.com/hashicorp/nomad/client/driver"
    13  	"github.com/hashicorp/nomad/nomad/mock"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  	"github.com/hashicorp/nomad/testutil"
    16  
    17  	ctestutil "github.com/hashicorp/nomad/client/testutil"
    18  )
    19  
    20  func testLogger() *log.Logger {
    21  	return log.New(os.Stderr, "", log.LstdFlags)
    22  }
    23  
    24  type MockTaskStateUpdater struct {
    25  	Count       int
    26  	Name        []string
    27  	Status      []string
    28  	Description []string
    29  }
    30  
    31  func (m *MockTaskStateUpdater) Update(name, status, desc string) {
    32  	m.Count += 1
    33  	m.Name = append(m.Name, name)
    34  	m.Status = append(m.Status, status)
    35  	m.Description = append(m.Description, desc)
    36  }
    37  
    38  func testTaskRunner() (*MockTaskStateUpdater, *TaskRunner) {
    39  	logger := testLogger()
    40  	conf := DefaultConfig()
    41  	conf.StateDir = os.TempDir()
    42  	conf.AllocDir = os.TempDir()
    43  	upd := &MockTaskStateUpdater{}
    44  	alloc := mock.Alloc()
    45  	task := alloc.Job.TaskGroups[0].Tasks[0]
    46  
    47  	// Initialize the port listing. This should be done by the offer process but
    48  	// we have a mock so that doesn't happen.
    49  	task.Resources.Networks[0].ReservedPorts = []int{80}
    50  
    51  	allocDir := allocdir.NewAllocDir(filepath.Join(conf.AllocDir, alloc.ID))
    52  	allocDir.Build([]*structs.Task{task})
    53  
    54  	ctx := driver.NewExecContext(allocDir)
    55  	tr := NewTaskRunner(logger, conf, upd.Update, ctx, alloc.ID, task)
    56  	return upd, tr
    57  }
    58  
    59  func TestTaskRunner_SimpleRun(t *testing.T) {
    60  	ctestutil.ExecCompatible(t)
    61  	upd, tr := testTaskRunner()
    62  	go tr.Run()
    63  	defer tr.Destroy()
    64  	defer tr.ctx.AllocDir.Destroy()
    65  
    66  	select {
    67  	case <-tr.WaitCh():
    68  	case <-time.After(2 * time.Second):
    69  		t.Fatalf("timeout")
    70  	}
    71  
    72  	if upd.Count != 2 {
    73  		t.Fatalf("should have 2 updates: %#v", upd)
    74  	}
    75  	if upd.Name[0] != tr.task.Name {
    76  		t.Fatalf("bad: %#v", upd.Name)
    77  	}
    78  	if upd.Status[0] != structs.AllocClientStatusRunning {
    79  		t.Fatalf("bad: %#v", upd.Status)
    80  	}
    81  	if upd.Description[0] != "task started" {
    82  		t.Fatalf("bad: %#v", upd.Description)
    83  	}
    84  
    85  	if upd.Name[1] != tr.task.Name {
    86  		t.Fatalf("bad: %#v", upd.Name)
    87  	}
    88  	if upd.Status[1] != structs.AllocClientStatusDead {
    89  		t.Fatalf("bad: %#v", upd.Status)
    90  	}
    91  	if upd.Description[1] != "task completed" {
    92  		t.Fatalf("bad: %#v", upd.Description)
    93  	}
    94  }
    95  
    96  func TestTaskRunner_Destroy(t *testing.T) {
    97  	ctestutil.ExecCompatible(t)
    98  	upd, tr := testTaskRunner()
    99  	defer tr.ctx.AllocDir.Destroy()
   100  
   101  	// Change command to ensure we run for a bit
   102  	tr.task.Config["command"] = "/bin/sleep"
   103  	tr.task.Config["args"] = "10"
   104  	go tr.Run()
   105  
   106  	// Begin the tear down
   107  	go func() {
   108  		time.Sleep(100 * time.Millisecond)
   109  		tr.Destroy()
   110  	}()
   111  
   112  	select {
   113  	case <-tr.WaitCh():
   114  	case <-time.After(2 * time.Second):
   115  		t.Fatalf("timeout")
   116  	}
   117  
   118  	if upd.Count != 2 {
   119  		t.Fatalf("should have 2 updates: %#v", upd)
   120  	}
   121  	if upd.Status[0] != structs.AllocClientStatusRunning {
   122  		t.Fatalf("bad: %#v", upd.Status)
   123  	}
   124  	if upd.Status[1] != structs.AllocClientStatusDead {
   125  		t.Fatalf("bad: %#v", upd.Status)
   126  	}
   127  	if !strings.Contains(upd.Description[1], "task failed") {
   128  		t.Fatalf("bad: %#v", upd.Description)
   129  	}
   130  }
   131  
   132  func TestTaskRunner_Update(t *testing.T) {
   133  	ctestutil.ExecCompatible(t)
   134  	_, tr := testTaskRunner()
   135  
   136  	// Change command to ensure we run for a bit
   137  	tr.task.Config["command"] = "/bin/sleep"
   138  	tr.task.Config["args"] = "10"
   139  	go tr.Run()
   140  	defer tr.Destroy()
   141  	defer tr.ctx.AllocDir.Destroy()
   142  
   143  	// Update the task definition
   144  	newTask := new(structs.Task)
   145  	*newTask = *tr.task
   146  	newTask.Driver = "foobar"
   147  	tr.Update(newTask)
   148  
   149  	// Wait for update to take place
   150  	testutil.WaitForResult(func() (bool, error) {
   151  		return tr.task == newTask, nil
   152  	}, func(err error) {
   153  		t.Fatalf("err: %v", err)
   154  	})
   155  }
   156  
   157  /*
   158  TODO: This test is disabled til a follow-up api changes the restore state interface.
   159  The driver/executor interface will be changed from Open to Cleanup, in which
   160  clean-up tears down previous allocs.
   161  
   162  func TestTaskRunner_SaveRestoreState(t *testing.T) {
   163  	upd, tr := testTaskRunner()
   164  
   165  	// Change command to ensure we run for a bit
   166  	tr.task.Config["command"] = "/bin/sleep"
   167  	tr.task.Config["args"] = "10"
   168  	go tr.Run()
   169  	defer tr.Destroy()
   170  
   171  	// Snapshot state
   172  	time.Sleep(200 * time.Millisecond)
   173  	err := tr.SaveState()
   174  	if err != nil {
   175  		t.Fatalf("err: %v", err)
   176  	}
   177  
   178  	// Create a new task runner
   179  	tr2 := NewTaskRunner(tr.logger, tr.config, upd.Update,
   180  		tr.ctx, tr.allocID, &structs.Task{Name: tr.task.Name})
   181  	err = tr2.RestoreState()
   182  	if err != nil {
   183  		t.Fatalf("err: %v", err)
   184  	}
   185  	go tr2.Run()
   186  	defer tr2.Destroy()
   187  
   188  	// Destroy and wait
   189  	tr2.Destroy()
   190  
   191  	select {
   192  	case <-tr.WaitCh():
   193  	case <-time.After(2 * time.Second):
   194  		t.Fatalf("timeout")
   195  	}
   196  }
   197  */