github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/client/driver/exec_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"strings"
    11  	"syscall"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/ncodes/nomad/client/config"
    16  	"github.com/ncodes/nomad/client/driver/env"
    17  	"github.com/ncodes/nomad/nomad/structs"
    18  	"github.com/ncodes/nomad/testutil"
    19  
    20  	ctestutils "github.com/ncodes/nomad/client/testutil"
    21  )
    22  
    23  func TestExecDriver_Fingerprint(t *testing.T) {
    24  	ctestutils.ExecCompatible(t)
    25  	task := &structs.Task{
    26  		Name:      "foo",
    27  		Driver:    "exec",
    28  		Resources: structs.DefaultResources(),
    29  	}
    30  	ctx := testDriverContexts(t, task)
    31  	defer ctx.AllocDir.Destroy()
    32  	d := NewExecDriver(ctx.DriverCtx)
    33  	node := &structs.Node{
    34  		Attributes: map[string]string{
    35  			"unique.cgroup.mountpoint": "/sys/fs/cgroup",
    36  		},
    37  	}
    38  	apply, err := d.Fingerprint(&config.Config{}, node)
    39  	if err != nil {
    40  		t.Fatalf("err: %v", err)
    41  	}
    42  	if !apply {
    43  		t.Fatalf("should apply")
    44  	}
    45  	if node.Attributes["driver.exec"] == "" {
    46  		t.Fatalf("missing driver")
    47  	}
    48  }
    49  
    50  func TestExecDriver_StartOpen_Wait(t *testing.T) {
    51  	ctestutils.ExecCompatible(t)
    52  	task := &structs.Task{
    53  		Name:   "sleep",
    54  		Driver: "exec",
    55  		Config: map[string]interface{}{
    56  			"command": "/bin/sleep",
    57  			"args":    []string{"5"},
    58  		},
    59  		LogConfig: &structs.LogConfig{
    60  			MaxFiles:      10,
    61  			MaxFileSizeMB: 10,
    62  		},
    63  		Resources: basicResources,
    64  	}
    65  
    66  	ctx := testDriverContexts(t, task)
    67  	defer ctx.AllocDir.Destroy()
    68  	d := NewExecDriver(ctx.DriverCtx)
    69  
    70  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
    71  		t.Fatalf("prestart err: %v", err)
    72  	}
    73  	handle, err := d.Start(ctx.ExecCtx, task)
    74  	if err != nil {
    75  		t.Fatalf("err: %v", err)
    76  	}
    77  	if handle == nil {
    78  		t.Fatalf("missing handle")
    79  	}
    80  
    81  	// Attempt to open
    82  	handle2, err := d.Open(ctx.ExecCtx, handle.ID())
    83  	if err != nil {
    84  		t.Fatalf("err: %v", err)
    85  	}
    86  	if handle2 == nil {
    87  		t.Fatalf("missing handle")
    88  	}
    89  
    90  	handle.Kill()
    91  	handle2.Kill()
    92  }
    93  
    94  func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) {
    95  	ctestutils.ExecCompatible(t)
    96  	task := &structs.Task{
    97  		Name:   "sleep",
    98  		Driver: "exec",
    99  		Config: map[string]interface{}{
   100  			"command": "/bin/sleep",
   101  			"args":    []string{"1000000"},
   102  		},
   103  		LogConfig: &structs.LogConfig{
   104  			MaxFiles:      10,
   105  			MaxFileSizeMB: 10,
   106  		},
   107  		Resources: basicResources,
   108  	}
   109  
   110  	ctx := testDriverContexts(t, task)
   111  	defer ctx.AllocDir.Destroy()
   112  	d := NewExecDriver(ctx.DriverCtx)
   113  
   114  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   115  		t.Fatalf("prestart err: %v", err)
   116  	}
   117  	handle, err := d.Start(ctx.ExecCtx, task)
   118  	if err != nil {
   119  		t.Fatalf("err: %v", err)
   120  	}
   121  	if handle == nil {
   122  		t.Fatalf("missing handle")
   123  	}
   124  	defer handle.Kill()
   125  
   126  	id := &execId{}
   127  	if err := json.Unmarshal([]byte(handle.ID()), id); err != nil {
   128  		t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err)
   129  	}
   130  	pluginPid := id.PluginConfig.Pid
   131  	proc, err := os.FindProcess(pluginPid)
   132  	if err != nil {
   133  		t.Fatalf("can't find plugin pid: %v", pluginPid)
   134  	}
   135  	if err := proc.Kill(); err != nil {
   136  		t.Fatalf("can't kill plugin pid: %v", err)
   137  	}
   138  
   139  	// Attempt to open
   140  	handle2, err := d.Open(ctx.ExecCtx, handle.ID())
   141  	if err == nil {
   142  		t.Fatalf("expected error")
   143  	}
   144  	if handle2 != nil {
   145  		handle2.Kill()
   146  		t.Fatalf("expected handle2 to be nil")
   147  	}
   148  
   149  	// Test if the userpid is still present
   150  	userProc, _ := os.FindProcess(id.UserPid)
   151  
   152  	for retry := 3; retry > 0; retry-- {
   153  		if err = userProc.Signal(syscall.Signal(0)); err != nil {
   154  			// Process is gone as expected; exit
   155  			return
   156  		}
   157  
   158  		// Killing processes is async; wait and check again
   159  		time.Sleep(time.Second)
   160  	}
   161  	if err = userProc.Signal(syscall.Signal(0)); err == nil {
   162  		t.Fatalf("expected user process to die")
   163  	}
   164  }
   165  
   166  func TestExecDriver_Start_Wait(t *testing.T) {
   167  	ctestutils.ExecCompatible(t)
   168  	task := &structs.Task{
   169  		Name:   "sleep",
   170  		Driver: "exec",
   171  		Config: map[string]interface{}{
   172  			"command": "/bin/sleep",
   173  			"args":    []string{"2"},
   174  		},
   175  		LogConfig: &structs.LogConfig{
   176  			MaxFiles:      10,
   177  			MaxFileSizeMB: 10,
   178  		},
   179  		Resources: basicResources,
   180  	}
   181  
   182  	ctx := testDriverContexts(t, task)
   183  	defer ctx.AllocDir.Destroy()
   184  	d := NewExecDriver(ctx.DriverCtx)
   185  
   186  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   187  		t.Fatalf("prestart err: %v", err)
   188  	}
   189  	handle, err := d.Start(ctx.ExecCtx, task)
   190  	if err != nil {
   191  		t.Fatalf("err: %v", err)
   192  	}
   193  	if handle == nil {
   194  		t.Fatalf("missing handle")
   195  	}
   196  
   197  	// Update should be a no-op
   198  	err = handle.Update(task)
   199  	if err != nil {
   200  		t.Fatalf("err: %v", err)
   201  	}
   202  
   203  	// Task should terminate quickly
   204  	select {
   205  	case res := <-handle.WaitCh():
   206  		if !res.Successful() {
   207  			t.Fatalf("err: %v", res)
   208  		}
   209  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   210  		t.Fatalf("timeout")
   211  	}
   212  }
   213  
   214  func TestExecDriver_Start_Wait_AllocDir(t *testing.T) {
   215  	ctestutils.ExecCompatible(t)
   216  
   217  	exp := []byte{'w', 'i', 'n'}
   218  	file := "output.txt"
   219  	task := &structs.Task{
   220  		Name:   "sleep",
   221  		Driver: "exec",
   222  		Config: map[string]interface{}{
   223  			"command": "/bin/bash",
   224  			"args": []string{
   225  				"-c",
   226  				fmt.Sprintf(`sleep 1; echo -n %s > ${%s}/%s`, string(exp), env.AllocDir, file),
   227  			},
   228  		},
   229  		LogConfig: &structs.LogConfig{
   230  			MaxFiles:      10,
   231  			MaxFileSizeMB: 10,
   232  		},
   233  		Resources: basicResources,
   234  	}
   235  
   236  	ctx := testDriverContexts(t, task)
   237  	defer ctx.AllocDir.Destroy()
   238  	d := NewExecDriver(ctx.DriverCtx)
   239  
   240  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   241  		t.Fatalf("prestart err: %v", err)
   242  	}
   243  	handle, err := d.Start(ctx.ExecCtx, task)
   244  	if err != nil {
   245  		t.Fatalf("err: %v", err)
   246  	}
   247  	if handle == nil {
   248  		t.Fatalf("missing handle")
   249  	}
   250  
   251  	// Task should terminate quickly
   252  	select {
   253  	case res := <-handle.WaitCh():
   254  		if !res.Successful() {
   255  			t.Fatalf("err: %v", res)
   256  		}
   257  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   258  		t.Fatalf("timeout")
   259  	}
   260  
   261  	// Check that data was written to the shared alloc directory.
   262  	outputFile := filepath.Join(ctx.AllocDir.SharedDir, file)
   263  	act, err := ioutil.ReadFile(outputFile)
   264  	if err != nil {
   265  		t.Fatalf("Couldn't read expected output: %v", err)
   266  	}
   267  
   268  	if !reflect.DeepEqual(act, exp) {
   269  		t.Fatalf("Command outputted %v; want %v", act, exp)
   270  	}
   271  }
   272  
   273  func TestExecDriver_Start_Kill_Wait(t *testing.T) {
   274  	ctestutils.ExecCompatible(t)
   275  	task := &structs.Task{
   276  		Name:   "sleep",
   277  		Driver: "exec",
   278  		Config: map[string]interface{}{
   279  			"command": "/bin/sleep",
   280  			"args":    []string{"100"},
   281  		},
   282  		LogConfig: &structs.LogConfig{
   283  			MaxFiles:      10,
   284  			MaxFileSizeMB: 10,
   285  		},
   286  		Resources:   basicResources,
   287  		KillTimeout: 10 * time.Second,
   288  	}
   289  
   290  	ctx := testDriverContexts(t, task)
   291  	defer ctx.AllocDir.Destroy()
   292  	d := NewExecDriver(ctx.DriverCtx)
   293  
   294  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   295  		t.Fatalf("prestart err: %v", err)
   296  	}
   297  	handle, err := d.Start(ctx.ExecCtx, task)
   298  	if err != nil {
   299  		t.Fatalf("err: %v", err)
   300  	}
   301  	if handle == nil {
   302  		t.Fatalf("missing handle")
   303  	}
   304  
   305  	go func() {
   306  		time.Sleep(100 * time.Millisecond)
   307  		err := handle.Kill()
   308  		if err != nil {
   309  			t.Fatalf("err: %v", err)
   310  		}
   311  	}()
   312  
   313  	// Task should terminate quickly
   314  	select {
   315  	case res := <-handle.WaitCh():
   316  		if res.Successful() {
   317  			t.Fatal("should err")
   318  		}
   319  	case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second):
   320  		t.Fatalf("timeout")
   321  	}
   322  }
   323  
   324  func TestExecDriver_Signal(t *testing.T) {
   325  	ctestutils.ExecCompatible(t)
   326  	task := &structs.Task{
   327  		Name:   "signal",
   328  		Driver: "exec",
   329  		Config: map[string]interface{}{
   330  			"command": "/bin/bash",
   331  			"args":    []string{"test.sh"},
   332  		},
   333  		LogConfig: &structs.LogConfig{
   334  			MaxFiles:      10,
   335  			MaxFileSizeMB: 10,
   336  		},
   337  		Resources:   basicResources,
   338  		KillTimeout: 10 * time.Second,
   339  	}
   340  
   341  	ctx := testDriverContexts(t, task)
   342  	defer ctx.AllocDir.Destroy()
   343  	d := NewExecDriver(ctx.DriverCtx)
   344  
   345  	testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh")
   346  	testData := []byte(`
   347  at_term() {
   348      echo 'Terminated.'
   349      exit 3
   350  }
   351  trap at_term USR1
   352  while true; do
   353      sleep 1
   354  done
   355  	`)
   356  	if err := ioutil.WriteFile(testFile, testData, 0777); err != nil {
   357  		t.Fatalf("Failed to write data: %v", err)
   358  	}
   359  
   360  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   361  		t.Fatalf("prestart err: %v", err)
   362  	}
   363  	handle, err := d.Start(ctx.ExecCtx, task)
   364  	if err != nil {
   365  		t.Fatalf("err: %v", err)
   366  	}
   367  	if handle == nil {
   368  		t.Fatalf("missing handle")
   369  	}
   370  
   371  	go func() {
   372  		time.Sleep(100 * time.Millisecond)
   373  		err := handle.Signal(syscall.SIGUSR1)
   374  		if err != nil {
   375  			t.Fatalf("err: %v", err)
   376  		}
   377  	}()
   378  
   379  	// Task should terminate quickly
   380  	select {
   381  	case res := <-handle.WaitCh():
   382  		if res.Successful() {
   383  			t.Fatal("should err")
   384  		}
   385  	case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second):
   386  		t.Fatalf("timeout")
   387  	}
   388  
   389  	// Check the log file to see it exited because of the signal
   390  	outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0")
   391  	act, err := ioutil.ReadFile(outputFile)
   392  	if err != nil {
   393  		t.Fatalf("Couldn't read expected output: %v", err)
   394  	}
   395  
   396  	exp := "Terminated."
   397  	if strings.TrimSpace(string(act)) != exp {
   398  		t.Logf("Read from %v", outputFile)
   399  		t.Fatalf("Command outputted %v; want %v", act, exp)
   400  	}
   401  }
   402  
   403  func TestExecDriverUser(t *testing.T) {
   404  	ctestutils.ExecCompatible(t)
   405  	task := &structs.Task{
   406  		Name:   "sleep",
   407  		Driver: "exec",
   408  		User:   "alice",
   409  		Config: map[string]interface{}{
   410  			"command": "/bin/sleep",
   411  			"args":    []string{"100"},
   412  		},
   413  		LogConfig: &structs.LogConfig{
   414  			MaxFiles:      10,
   415  			MaxFileSizeMB: 10,
   416  		},
   417  		Resources:   basicResources,
   418  		KillTimeout: 10 * time.Second,
   419  	}
   420  
   421  	ctx := testDriverContexts(t, task)
   422  	defer ctx.AllocDir.Destroy()
   423  	d := NewExecDriver(ctx.DriverCtx)
   424  
   425  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   426  		t.Fatalf("prestart err: %v", err)
   427  	}
   428  	handle, err := d.Start(ctx.ExecCtx, task)
   429  	if err == nil {
   430  		handle.Kill()
   431  		t.Fatalf("Should've failed")
   432  	}
   433  	msg := "user alice"
   434  	if !strings.Contains(err.Error(), msg) {
   435  		t.Fatalf("Expecting '%v' in '%v'", msg, err)
   436  	}
   437  }