github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/client/driver/executor/test_harness_test.go (about)

     1  package executor
     2  
     3  import (
     4  	"io/ioutil"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/hashicorp/nomad/client/allocdir"
    12  	"github.com/hashicorp/nomad/client/driver/env"
    13  	"github.com/hashicorp/nomad/helper/testtask"
    14  	"github.com/hashicorp/nomad/nomad/mock"
    15  	"github.com/hashicorp/nomad/nomad/structs"
    16  	"github.com/hashicorp/nomad/testutil"
    17  )
    18  
    19  func TestMain(m *testing.M) {
    20  	if !testtask.Run() {
    21  		os.Exit(m.Run())
    22  	}
    23  }
    24  
    25  var (
    26  	constraint = &structs.Resources{
    27  		CPU:      250,
    28  		MemoryMB: 256,
    29  		Networks: []*structs.NetworkResource{
    30  			&structs.NetworkResource{
    31  				MBits:        50,
    32  				DynamicPorts: []structs.Port{{Label: "http"}},
    33  			},
    34  		},
    35  	}
    36  )
    37  
    38  func mockAllocDir(t *testing.T) (string, *allocdir.AllocDir) {
    39  	alloc := mock.Alloc()
    40  	task := alloc.Job.TaskGroups[0].Tasks[0]
    41  
    42  	allocDir := allocdir.NewAllocDir(filepath.Join(os.TempDir(), alloc.ID))
    43  	if err := allocDir.Build([]*structs.Task{task}); err != nil {
    44  		log.Panicf("allocDir.Build() failed: %v", err)
    45  	}
    46  
    47  	return task.Name, allocDir
    48  }
    49  
    50  func testExecutorContext() *ExecutorContext {
    51  	taskEnv := env.NewTaskEnvironment(mock.Node())
    52  	return &ExecutorContext{taskEnv: taskEnv}
    53  }
    54  
    55  func testExecutor(t *testing.T, buildExecutor func(*ExecutorContext) Executor, compatible func(*testing.T)) {
    56  	if compatible != nil {
    57  		compatible(t)
    58  	}
    59  
    60  	command := func(name string, args ...string) Executor {
    61  		ctx := testExecutorContext()
    62  		e := buildExecutor(ctx)
    63  		SetCommand(e, name, args)
    64  		testtask.SetEnv(ctx.taskEnv)
    65  		return e
    66  	}
    67  
    68  	Executor_Start_Invalid(t, command)
    69  	Executor_Start_Wait_Failure_Code(t, command)
    70  	Executor_Start_Wait(t, command)
    71  	Executor_Start_Kill(t, command)
    72  	Executor_Open(t, command, buildExecutor)
    73  	Executor_Open_Invalid(t, command, buildExecutor)
    74  }
    75  
    76  type buildExecCommand func(name string, args ...string) Executor
    77  
    78  func Executor_Start_Invalid(t *testing.T, command buildExecCommand) {
    79  	invalid := "/bin/foobar"
    80  	e := command(invalid, "1")
    81  
    82  	if err := e.Limit(constraint); err != nil {
    83  		log.Panicf("Limit() failed: %v", err)
    84  	}
    85  
    86  	task, alloc := mockAllocDir(t)
    87  	defer alloc.Destroy()
    88  	if err := e.ConfigureTaskDir(task, alloc); err != nil {
    89  		log.Panicf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err)
    90  	}
    91  
    92  	if err := e.Start(); err == nil {
    93  		log.Panicf("Start(%v) should have failed", invalid)
    94  	}
    95  }
    96  
    97  func Executor_Start_Wait_Failure_Code(t *testing.T, command buildExecCommand) {
    98  	e := command(testtask.Path(), "fail")
    99  
   100  	if err := e.Limit(constraint); err != nil {
   101  		log.Panicf("Limit() failed: %v", err)
   102  	}
   103  
   104  	task, alloc := mockAllocDir(t)
   105  	defer alloc.Destroy()
   106  	if err := e.ConfigureTaskDir(task, alloc); err != nil {
   107  		log.Panicf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err)
   108  	}
   109  
   110  	if err := e.Start(); err != nil {
   111  		log.Panicf("Start() failed: %v", err)
   112  	}
   113  
   114  	if err := e.Wait(); err == nil {
   115  		log.Panicf("Wait() should have failed")
   116  	}
   117  }
   118  
   119  func Executor_Start_Wait(t *testing.T, command buildExecCommand) {
   120  	task, alloc := mockAllocDir(t)
   121  	defer alloc.Destroy()
   122  
   123  	taskDir, ok := alloc.TaskDirs[task]
   124  	if !ok {
   125  		log.Panicf("No task directory found for task %v", task)
   126  	}
   127  
   128  	expected := "hello world"
   129  	file := filepath.Join(allocdir.TaskLocal, "output.txt")
   130  	absFilePath := filepath.Join(taskDir, file)
   131  	e := command(testtask.Path(), "sleep", "1s", "write", expected, file)
   132  
   133  	if err := e.Limit(constraint); err != nil {
   134  		log.Panicf("Limit() failed: %v", err)
   135  	}
   136  
   137  	if err := e.ConfigureTaskDir(task, alloc); err != nil {
   138  		log.Panicf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err)
   139  	}
   140  
   141  	if err := e.Start(); err != nil {
   142  		log.Panicf("Start() failed: %v", err)
   143  	}
   144  
   145  	if res := e.Wait(); !res.Successful() {
   146  		log.Panicf("Wait() failed: %v", res)
   147  	}
   148  
   149  	output, err := ioutil.ReadFile(absFilePath)
   150  	if err != nil {
   151  		log.Panicf("Couldn't read file %v", absFilePath)
   152  	}
   153  
   154  	act := string(output)
   155  	if act != expected {
   156  		log.Panicf("Command output incorrectly: want %v; got %v", expected, act)
   157  	}
   158  }
   159  
   160  func Executor_Start_Kill(t *testing.T, command buildExecCommand) {
   161  	task, alloc := mockAllocDir(t)
   162  	defer alloc.Destroy()
   163  
   164  	taskDir, ok := alloc.TaskDirs[task]
   165  	if !ok {
   166  		log.Panicf("No task directory found for task %v", task)
   167  	}
   168  
   169  	filePath := filepath.Join(taskDir, "output")
   170  	e := command(testtask.Path(), "sleep", "1s", "write", "failure", filePath)
   171  
   172  	if err := e.Limit(constraint); err != nil {
   173  		log.Panicf("Limit() failed: %v", err)
   174  	}
   175  
   176  	if err := e.ConfigureTaskDir(task, alloc); err != nil {
   177  		log.Panicf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err)
   178  	}
   179  
   180  	if err := e.Start(); err != nil {
   181  		log.Panicf("Start() failed: %v", err)
   182  	}
   183  
   184  	if err := e.Shutdown(); err != nil {
   185  		log.Panicf("Shutdown() failed: %v", err)
   186  	}
   187  
   188  	time.Sleep(time.Duration(testutil.TestMultiplier()*2) * time.Second)
   189  
   190  	// Check that the file doesn't exist.
   191  	if _, err := os.Stat(filePath); err == nil {
   192  		log.Panicf("Stat(%v) should have failed: task not killed", filePath)
   193  	}
   194  }
   195  
   196  func Executor_Open(t *testing.T, command buildExecCommand, newExecutor func(*ExecutorContext) Executor) {
   197  	task, alloc := mockAllocDir(t)
   198  	defer alloc.Destroy()
   199  
   200  	taskDir, ok := alloc.TaskDirs[task]
   201  	if !ok {
   202  		log.Panicf("No task directory found for task %v", task)
   203  	}
   204  
   205  	expected := "hello world"
   206  	file := filepath.Join(allocdir.TaskLocal, "output.txt")
   207  	absFilePath := filepath.Join(taskDir, file)
   208  	e := command(testtask.Path(), "sleep", "1s", "write", expected, file)
   209  
   210  	if err := e.Limit(constraint); err != nil {
   211  		log.Panicf("Limit() failed: %v", err)
   212  	}
   213  
   214  	if err := e.ConfigureTaskDir(task, alloc); err != nil {
   215  		log.Panicf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err)
   216  	}
   217  
   218  	if err := e.Start(); err != nil {
   219  		log.Panicf("Start() failed: %v", err)
   220  	}
   221  
   222  	id, err := e.ID()
   223  	if err != nil {
   224  		log.Panicf("ID() failed: %v", err)
   225  	}
   226  
   227  	e2 := newExecutor(testExecutorContext())
   228  	if err := e2.Open(id); err != nil {
   229  		log.Panicf("Open(%v) failed: %v", id, err)
   230  	}
   231  
   232  	if res := e2.Wait(); !res.Successful() {
   233  		log.Panicf("Wait() failed: %v", res)
   234  	}
   235  
   236  	output, err := ioutil.ReadFile(absFilePath)
   237  	if err != nil {
   238  		log.Panicf("Couldn't read file %v", absFilePath)
   239  	}
   240  
   241  	act := string(output)
   242  	if act != expected {
   243  		log.Panicf("Command output incorrectly: want %v; got %v", expected, act)
   244  	}
   245  }
   246  
   247  func Executor_Open_Invalid(t *testing.T, command buildExecCommand, newExecutor func(*ExecutorContext) Executor) {
   248  	task, alloc := mockAllocDir(t)
   249  	e := command(testtask.Path(), "echo", "foo")
   250  
   251  	if err := e.Limit(constraint); err != nil {
   252  		log.Panicf("Limit() failed: %v", err)
   253  	}
   254  
   255  	if err := e.ConfigureTaskDir(task, alloc); err != nil {
   256  		log.Panicf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err)
   257  	}
   258  
   259  	if err := e.Start(); err != nil {
   260  		log.Panicf("Start() failed: %v", err)
   261  	}
   262  
   263  	id, err := e.ID()
   264  	if err != nil {
   265  		log.Panicf("ID() failed: %v", err)
   266  	}
   267  
   268  	// Kill the task because some OSes (windows) will not let us destroy the
   269  	// alloc (below) if the task is still running.
   270  	if err := e.ForceStop(); err != nil {
   271  		log.Panicf("e.ForceStop() failed: %v", err)
   272  	}
   273  
   274  	// Wait until process is actually gone, we don't care what the result was.
   275  	e.Wait()
   276  
   277  	// Destroy the allocdir which removes the exit code.
   278  	if err := alloc.Destroy(); err != nil {
   279  		log.Panicf("alloc.Destroy() failed: %v", err)
   280  	}
   281  
   282  	e2 := newExecutor(testExecutorContext())
   283  	if err := e2.Open(id); err == nil {
   284  		log.Panicf("Open(%v) should have failed", id)
   285  	}
   286  }