github.com/bigcommerce/nomad@v0.9.3-bc/drivers/rawexec/driver_unix_test.go (about)

     1  // +build !windows
     2  
     3  package rawexec
     4  
     5  import (
     6  	"context"
     7  	"runtime"
     8  	"testing"
     9  
    10  	"fmt"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/hashicorp/nomad/helper/testlog"
    17  	"github.com/hashicorp/nomad/helper/testtask"
    18  	"github.com/hashicorp/nomad/helper/uuid"
    19  	basePlug "github.com/hashicorp/nomad/plugins/base"
    20  	"github.com/hashicorp/nomad/plugins/drivers"
    21  	dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
    22  	"github.com/hashicorp/nomad/testutil"
    23  	"github.com/stretchr/testify/require"
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  func TestRawExecDriver_User(t *testing.T) {
    28  	t.Parallel()
    29  	if runtime.GOOS != "linux" {
    30  		t.Skip("Linux only test")
    31  	}
    32  	require := require.New(t)
    33  
    34  	d := NewRawExecDriver(testlog.HCLogger(t))
    35  	harness := dtestutil.NewDriverHarness(t, d)
    36  
    37  	task := &drivers.TaskConfig{
    38  		ID:   uuid.Generate(),
    39  		Name: "sleep",
    40  		User: "alice",
    41  	}
    42  
    43  	cleanup := harness.MkAllocDir(task, false)
    44  	defer cleanup()
    45  
    46  	tc := &TaskConfig{
    47  		Command: testtask.Path(),
    48  		Args:    []string{"sleep", "45s"},
    49  	}
    50  	require.NoError(task.EncodeConcreteDriverConfig(&tc))
    51  	testtask.SetTaskConfigEnv(task)
    52  
    53  	_, _, err := harness.StartTask(task)
    54  	require.Error(err)
    55  	msg := "unknown user alice"
    56  	require.Contains(err.Error(), msg)
    57  }
    58  
    59  func TestRawExecDriver_Signal(t *testing.T) {
    60  	t.Parallel()
    61  	if runtime.GOOS != "linux" {
    62  		t.Skip("Linux only test")
    63  	}
    64  	require := require.New(t)
    65  
    66  	d := NewRawExecDriver(testlog.HCLogger(t))
    67  	harness := dtestutil.NewDriverHarness(t, d)
    68  
    69  	task := &drivers.TaskConfig{
    70  		ID:   uuid.Generate(),
    71  		Name: "signal",
    72  	}
    73  
    74  	cleanup := harness.MkAllocDir(task, true)
    75  	defer cleanup()
    76  
    77  	tc := &TaskConfig{
    78  		Command: "/bin/bash",
    79  		Args:    []string{"test.sh"},
    80  	}
    81  	require.NoError(task.EncodeConcreteDriverConfig(&tc))
    82  	testtask.SetTaskConfigEnv(task)
    83  
    84  	testFile := filepath.Join(task.TaskDir().Dir, "test.sh")
    85  	testData := []byte(`
    86  at_term() {
    87      echo 'Terminated.'
    88      exit 3
    89  }
    90  trap at_term USR1
    91  while true; do
    92      sleep 1
    93  done
    94  	`)
    95  	require.NoError(ioutil.WriteFile(testFile, testData, 0777))
    96  
    97  	_, _, err := harness.StartTask(task)
    98  	require.NoError(err)
    99  
   100  	go func() {
   101  		time.Sleep(100 * time.Millisecond)
   102  		require.NoError(harness.SignalTask(task.ID, "SIGUSR1"))
   103  	}()
   104  
   105  	// Task should terminate quickly
   106  	waitCh, err := harness.WaitTask(context.Background(), task.ID)
   107  	require.NoError(err)
   108  	select {
   109  	case res := <-waitCh:
   110  		require.False(res.Successful())
   111  		require.Equal(3, res.ExitCode)
   112  	case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second):
   113  		require.Fail("WaitTask timeout")
   114  	}
   115  
   116  	// Check the log file to see it exited because of the signal
   117  	outputFile := filepath.Join(task.TaskDir().LogDir, "signal.stdout.0")
   118  	exp := "Terminated."
   119  	testutil.WaitForResult(func() (bool, error) {
   120  		act, err := ioutil.ReadFile(outputFile)
   121  		if err != nil {
   122  			return false, fmt.Errorf("Couldn't read expected output: %v", err)
   123  		}
   124  
   125  		if strings.TrimSpace(string(act)) != exp {
   126  			t.Logf("Read from %v", outputFile)
   127  			return false, fmt.Errorf("Command outputted %v; want %v", act, exp)
   128  		}
   129  		return true, nil
   130  	}, func(err error) { require.NoError(err) })
   131  }
   132  
   133  func TestRawExecDriver_StartWaitStop(t *testing.T) {
   134  	t.Parallel()
   135  	require := require.New(t)
   136  
   137  	d := NewRawExecDriver(testlog.HCLogger(t))
   138  	harness := dtestutil.NewDriverHarness(t, d)
   139  	defer harness.Kill()
   140  
   141  	// Disable cgroups so test works without root
   142  	config := &Config{NoCgroups: true}
   143  	var data []byte
   144  	require.NoError(basePlug.MsgPackEncode(&data, config))
   145  	bconfig := &basePlug.Config{PluginConfig: data}
   146  	require.NoError(harness.SetConfig(bconfig))
   147  
   148  	task := &drivers.TaskConfig{
   149  		ID:   uuid.Generate(),
   150  		Name: "test",
   151  	}
   152  
   153  	taskConfig := map[string]interface{}{}
   154  	taskConfig["command"] = testtask.Path()
   155  	taskConfig["args"] = []string{"sleep", "100s"}
   156  
   157  	require.NoError(task.EncodeConcreteDriverConfig(&taskConfig))
   158  
   159  	cleanup := harness.MkAllocDir(task, false)
   160  	defer cleanup()
   161  
   162  	handle, _, err := harness.StartTask(task)
   163  	require.NoError(err)
   164  
   165  	ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
   166  	require.NoError(err)
   167  
   168  	require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second))
   169  
   170  	go func() {
   171  		harness.StopTask(task.ID, 2*time.Second, "SIGINT")
   172  	}()
   173  
   174  	select {
   175  	case result := <-ch:
   176  		require.Equal(int(unix.SIGINT), result.Signal)
   177  	case <-time.After(10 * time.Second):
   178  		require.Fail("timeout waiting for task to shutdown")
   179  	}
   180  
   181  	// Ensure that the task is marked as dead, but account
   182  	// for WaitTask() closing channel before internal state is updated
   183  	testutil.WaitForResult(func() (bool, error) {
   184  		status, err := harness.InspectTask(task.ID)
   185  		if err != nil {
   186  			return false, fmt.Errorf("inspecting task failed: %v", err)
   187  		}
   188  		if status.State != drivers.TaskStateExited {
   189  			return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State)
   190  		}
   191  
   192  		return true, nil
   193  	}, func(err error) {
   194  		require.NoError(err)
   195  	})
   196  
   197  	require.NoError(harness.DestroyTask(task.ID, true))
   198  }
   199  
   200  func TestRawExec_ExecTaskStreaming(t *testing.T) {
   201  	t.Parallel()
   202  	if runtime.GOOS == "darwin" {
   203  		t.Skip("skip running exec tasks on darwin as darwin has restrictions on starting tty shells")
   204  	}
   205  	require := require.New(t)
   206  
   207  	d := NewRawExecDriver(testlog.HCLogger(t))
   208  	harness := dtestutil.NewDriverHarness(t, d)
   209  	defer harness.Kill()
   210  
   211  	task := &drivers.TaskConfig{
   212  		ID:   uuid.Generate(),
   213  		Name: "sleep",
   214  	}
   215  
   216  	cleanup := harness.MkAllocDir(task, false)
   217  	defer cleanup()
   218  
   219  	tc := &TaskConfig{
   220  		Command: testtask.Path(),
   221  		Args:    []string{"sleep", "9000s"},
   222  	}
   223  	require.NoError(task.EncodeConcreteDriverConfig(&tc))
   224  	testtask.SetTaskConfigEnv(task)
   225  
   226  	_, _, err := harness.StartTask(task)
   227  	require.NoError(err)
   228  	defer d.DestroyTask(task.ID, true)
   229  
   230  	dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID)
   231  
   232  }