github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/driver/exec_unix_test.go (about)

     1  // +build !windows
     2  
     3  package driver
     4  
     5  import (
     6  	"encoding/json"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"syscall"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/hashicorp/nomad/nomad/structs"
    16  	"github.com/hashicorp/nomad/testutil"
    17  
    18  	ctestutils "github.com/hashicorp/nomad/client/testutil"
    19  )
    20  
    21  func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) {
    22  	ctestutils.ExecCompatible(t)
    23  	task := &structs.Task{
    24  		Name:   "sleep",
    25  		Driver: "exec",
    26  		Config: map[string]interface{}{
    27  			"command": "/bin/sleep",
    28  			"args":    []string{"1000000"},
    29  		},
    30  		LogConfig: &structs.LogConfig{
    31  			MaxFiles:      10,
    32  			MaxFileSizeMB: 10,
    33  		},
    34  		Resources: basicResources,
    35  	}
    36  
    37  	ctx := testDriverContexts(t, task)
    38  	defer ctx.AllocDir.Destroy()
    39  	d := NewExecDriver(ctx.DriverCtx)
    40  
    41  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
    42  		t.Fatalf("prestart err: %v", err)
    43  	}
    44  	handle, err := d.Start(ctx.ExecCtx, task)
    45  	if err != nil {
    46  		t.Fatalf("err: %v", err)
    47  	}
    48  	if handle == nil {
    49  		t.Fatalf("missing handle")
    50  	}
    51  	defer handle.Kill()
    52  
    53  	id := &execId{}
    54  	if err := json.Unmarshal([]byte(handle.ID()), id); err != nil {
    55  		t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err)
    56  	}
    57  	pluginPid := id.PluginConfig.Pid
    58  	proc, err := os.FindProcess(pluginPid)
    59  	if err != nil {
    60  		t.Fatalf("can't find plugin pid: %v", pluginPid)
    61  	}
    62  	if err := proc.Kill(); err != nil {
    63  		t.Fatalf("can't kill plugin pid: %v", err)
    64  	}
    65  
    66  	// Attempt to open
    67  	handle2, err := d.Open(ctx.ExecCtx, handle.ID())
    68  	if err == nil {
    69  		t.Fatalf("expected error")
    70  	}
    71  	if handle2 != nil {
    72  		handle2.Kill()
    73  		t.Fatalf("expected handle2 to be nil")
    74  	}
    75  
    76  	// Test if the userpid is still present
    77  	userProc, _ := os.FindProcess(id.UserPid)
    78  
    79  	for retry := 3; retry > 0; retry-- {
    80  		if err = userProc.Signal(syscall.Signal(0)); err != nil {
    81  			// Process is gone as expected; exit
    82  			return
    83  		}
    84  
    85  		// Killing processes is async; wait and check again
    86  		time.Sleep(time.Second)
    87  	}
    88  	if err = userProc.Signal(syscall.Signal(0)); err == nil {
    89  		t.Fatalf("expected user process to die")
    90  	}
    91  }
    92  
    93  func TestExecDriver_Signal(t *testing.T) {
    94  	ctestutils.ExecCompatible(t)
    95  	task := &structs.Task{
    96  		Name:   "signal",
    97  		Driver: "exec",
    98  		Config: map[string]interface{}{
    99  			"command": "/bin/bash",
   100  			"args":    []string{"test.sh"},
   101  		},
   102  		LogConfig: &structs.LogConfig{
   103  			MaxFiles:      10,
   104  			MaxFileSizeMB: 10,
   105  		},
   106  		Resources:   basicResources,
   107  		KillTimeout: 10 * time.Second,
   108  	}
   109  
   110  	ctx := testDriverContexts(t, task)
   111  	defer ctx.AllocDir.Destroy()
   112  	d := NewExecDriver(ctx.DriverCtx)
   113  
   114  	testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh")
   115  	testData := []byte(`
   116  at_term() {
   117      echo 'Terminated.'
   118      exit 3
   119  }
   120  trap at_term USR1
   121  while true; do
   122      sleep 1
   123  done
   124  	`)
   125  	if err := ioutil.WriteFile(testFile, testData, 0777); err != nil {
   126  		t.Fatalf("Failed to write data: %v", err)
   127  	}
   128  
   129  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   130  		t.Fatalf("prestart err: %v", err)
   131  	}
   132  	handle, err := d.Start(ctx.ExecCtx, task)
   133  	if err != nil {
   134  		t.Fatalf("err: %v", err)
   135  	}
   136  	if handle == nil {
   137  		t.Fatalf("missing handle")
   138  	}
   139  
   140  	go func() {
   141  		time.Sleep(100 * time.Millisecond)
   142  		err := handle.Signal(syscall.SIGUSR1)
   143  		if err != nil {
   144  			t.Fatalf("err: %v", err)
   145  		}
   146  	}()
   147  
   148  	// Task should terminate quickly
   149  	select {
   150  	case res := <-handle.WaitCh():
   151  		if res.Successful() {
   152  			t.Fatal("should err")
   153  		}
   154  	case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second):
   155  		t.Fatalf("timeout")
   156  	}
   157  
   158  	// Check the log file to see it exited because of the signal
   159  	outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0")
   160  	act, err := ioutil.ReadFile(outputFile)
   161  	if err != nil {
   162  		t.Fatalf("Couldn't read expected output: %v", err)
   163  	}
   164  
   165  	exp := "Terminated."
   166  	if strings.TrimSpace(string(act)) != exp {
   167  		t.Logf("Read from %v", outputFile)
   168  		t.Fatalf("Command outputted %v; want %v", act, exp)
   169  	}
   170  }