github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/raw_exec_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"path/filepath"
     9  	"reflect"
    10  	"runtime"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/hashicorp/nomad/client/config"
    16  	"github.com/hashicorp/nomad/client/driver/env"
    17  	"github.com/hashicorp/nomad/helper/testtask"
    18  	"github.com/hashicorp/nomad/nomad/structs"
    19  	"github.com/hashicorp/nomad/testutil"
    20  )
    21  
    22  func TestRawExecDriver_Fingerprint(t *testing.T) {
    23  	t.Parallel()
    24  	task := &structs.Task{
    25  		Name:      "foo",
    26  		Driver:    "raw_exec",
    27  		Resources: structs.DefaultResources(),
    28  	}
    29  	ctx := testDriverContexts(t, task)
    30  	defer ctx.AllocDir.Destroy()
    31  	d := NewRawExecDriver(ctx.DriverCtx)
    32  	node := &structs.Node{
    33  		Attributes: make(map[string]string),
    34  	}
    35  
    36  	// Disable raw exec.
    37  	cfg := &config.Config{Options: map[string]string{rawExecConfigOption: "false"}}
    38  
    39  	apply, err := d.Fingerprint(cfg, node)
    40  	if err != nil {
    41  		t.Fatalf("err: %v", err)
    42  	}
    43  	if apply {
    44  		t.Fatalf("should not apply")
    45  	}
    46  	if node.Attributes["driver.raw_exec"] != "" {
    47  		t.Fatalf("driver incorrectly enabled")
    48  	}
    49  
    50  	// Enable raw exec.
    51  	cfg.Options[rawExecConfigOption] = "true"
    52  	apply, err = d.Fingerprint(cfg, node)
    53  	if err != nil {
    54  		t.Fatalf("err: %v", err)
    55  	}
    56  	if !apply {
    57  		t.Fatalf("should apply")
    58  	}
    59  	if node.Attributes["driver.raw_exec"] != "1" {
    60  		t.Fatalf("driver not enabled")
    61  	}
    62  }
    63  
    64  func TestRawExecDriver_StartOpen_Wait(t *testing.T) {
    65  	t.Parallel()
    66  	task := &structs.Task{
    67  		Name:   "sleep",
    68  		Driver: "raw_exec",
    69  		Config: map[string]interface{}{
    70  			"command": testtask.Path(),
    71  			"args":    []string{"sleep", "1s"},
    72  		},
    73  		LogConfig: &structs.LogConfig{
    74  			MaxFiles:      10,
    75  			MaxFileSizeMB: 10,
    76  		},
    77  		Resources: basicResources,
    78  	}
    79  	testtask.SetTaskEnv(task)
    80  	ctx := testDriverContexts(t, task)
    81  	defer ctx.AllocDir.Destroy()
    82  	d := NewRawExecDriver(ctx.DriverCtx)
    83  
    84  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
    85  		t.Fatalf("prestart err: %v", err)
    86  	}
    87  	resp, err := d.Start(ctx.ExecCtx, task)
    88  	if err != nil {
    89  		t.Fatalf("err: %v", err)
    90  	}
    91  
    92  	// Attempt to open
    93  	handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID())
    94  	if err != nil {
    95  		t.Fatalf("err: %v", err)
    96  	}
    97  	if handle2 == nil {
    98  		t.Fatalf("missing handle")
    99  	}
   100  
   101  	// Task should terminate quickly
   102  	select {
   103  	case <-handle2.WaitCh():
   104  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   105  		t.Fatalf("timeout")
   106  	}
   107  	resp.Handle.Kill()
   108  	handle2.Kill()
   109  }
   110  
   111  func TestRawExecDriver_Start_Wait(t *testing.T) {
   112  	t.Parallel()
   113  	task := &structs.Task{
   114  		Name:   "sleep",
   115  		Driver: "raw_exec",
   116  		Config: map[string]interface{}{
   117  			"command": testtask.Path(),
   118  			"args":    []string{"sleep", "1s"},
   119  		},
   120  		LogConfig: &structs.LogConfig{
   121  			MaxFiles:      10,
   122  			MaxFileSizeMB: 10,
   123  		},
   124  		Resources: basicResources,
   125  	}
   126  	testtask.SetTaskEnv(task)
   127  	ctx := testDriverContexts(t, task)
   128  	defer ctx.AllocDir.Destroy()
   129  	d := NewRawExecDriver(ctx.DriverCtx)
   130  
   131  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   132  		t.Fatalf("prestart err: %v", err)
   133  	}
   134  	resp, err := d.Start(ctx.ExecCtx, task)
   135  	if err != nil {
   136  		t.Fatalf("err: %v", err)
   137  	}
   138  
   139  	// Update should be a no-op
   140  	err = resp.Handle.Update(task)
   141  	if err != nil {
   142  		t.Fatalf("err: %v", err)
   143  	}
   144  
   145  	// Task should terminate quickly
   146  	select {
   147  	case res := <-resp.Handle.WaitCh():
   148  		if !res.Successful() {
   149  			t.Fatalf("err: %v", res)
   150  		}
   151  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   152  		t.Fatalf("timeout")
   153  	}
   154  }
   155  
   156  func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) {
   157  	t.Parallel()
   158  	exp := []byte("win")
   159  	file := "output.txt"
   160  	outPath := fmt.Sprintf(`${%s}/%s`, env.AllocDir, file)
   161  	task := &structs.Task{
   162  		Name:   "sleep",
   163  		Driver: "raw_exec",
   164  		Config: map[string]interface{}{
   165  			"command": testtask.Path(),
   166  			"args": []string{
   167  				"sleep", "1s",
   168  				"write", string(exp), outPath,
   169  			},
   170  		},
   171  		LogConfig: &structs.LogConfig{
   172  			MaxFiles:      10,
   173  			MaxFileSizeMB: 10,
   174  		},
   175  		Resources: basicResources,
   176  	}
   177  	testtask.SetTaskEnv(task)
   178  
   179  	ctx := testDriverContexts(t, task)
   180  	defer ctx.AllocDir.Destroy()
   181  	d := NewRawExecDriver(ctx.DriverCtx)
   182  
   183  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   184  		t.Fatalf("prestart err: %v", err)
   185  	}
   186  	resp, err := d.Start(ctx.ExecCtx, task)
   187  	if err != nil {
   188  		t.Fatalf("err: %v", err)
   189  	}
   190  
   191  	// Task should terminate quickly
   192  	select {
   193  	case res := <-resp.Handle.WaitCh():
   194  		if !res.Successful() {
   195  			t.Fatalf("err: %v", res)
   196  		}
   197  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   198  		t.Fatalf("timeout")
   199  	}
   200  
   201  	// Check that data was written to the shared alloc directory.
   202  	outputFile := filepath.Join(ctx.AllocDir.SharedDir, file)
   203  	act, err := ioutil.ReadFile(outputFile)
   204  	if err != nil {
   205  		t.Fatalf("Couldn't read expected output: %v", err)
   206  	}
   207  
   208  	if !reflect.DeepEqual(act, exp) {
   209  		t.Fatalf("Command outputted %v; want %v", act, exp)
   210  	}
   211  }
   212  
   213  func TestRawExecDriver_Start_Kill_Wait(t *testing.T) {
   214  	t.Parallel()
   215  	task := &structs.Task{
   216  		Name:   "sleep",
   217  		Driver: "raw_exec",
   218  		Config: map[string]interface{}{
   219  			"command": testtask.Path(),
   220  			"args":    []string{"sleep", "45s"},
   221  		},
   222  		LogConfig: &structs.LogConfig{
   223  			MaxFiles:      10,
   224  			MaxFileSizeMB: 10,
   225  		},
   226  		Resources: basicResources,
   227  	}
   228  	testtask.SetTaskEnv(task)
   229  
   230  	ctx := testDriverContexts(t, task)
   231  	defer ctx.AllocDir.Destroy()
   232  	d := NewRawExecDriver(ctx.DriverCtx)
   233  
   234  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   235  		t.Fatalf("prestart err: %v", err)
   236  	}
   237  	resp, err := d.Start(ctx.ExecCtx, task)
   238  	if err != nil {
   239  		t.Fatalf("err: %v", err)
   240  	}
   241  
   242  	go func() {
   243  		time.Sleep(1 * time.Second)
   244  		err := resp.Handle.Kill()
   245  
   246  		// Can't rely on the ordering between wait and kill on travis...
   247  		if !testutil.IsTravis() && err != nil {
   248  			t.Fatalf("err: %v", err)
   249  		}
   250  	}()
   251  
   252  	// Task should terminate quickly
   253  	select {
   254  	case res := <-resp.Handle.WaitCh():
   255  		if res.Successful() {
   256  			t.Fatal("should err")
   257  		}
   258  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   259  		t.Fatalf("timeout")
   260  	}
   261  }
   262  
   263  func TestRawExecDriverUser(t *testing.T) {
   264  	t.Parallel()
   265  	if runtime.GOOS != "linux" {
   266  		t.Skip("Linux only test")
   267  	}
   268  	task := &structs.Task{
   269  		Name:   "sleep",
   270  		Driver: "raw_exec",
   271  		User:   "alice",
   272  		Config: map[string]interface{}{
   273  			"command": testtask.Path(),
   274  			"args":    []string{"sleep", "45s"},
   275  		},
   276  		LogConfig: &structs.LogConfig{
   277  			MaxFiles:      10,
   278  			MaxFileSizeMB: 10,
   279  		},
   280  		Resources: basicResources,
   281  	}
   282  	testtask.SetTaskEnv(task)
   283  
   284  	ctx := testDriverContexts(t, task)
   285  	defer ctx.AllocDir.Destroy()
   286  	d := NewRawExecDriver(ctx.DriverCtx)
   287  
   288  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   289  		t.Fatalf("prestart err: %v", err)
   290  	}
   291  	resp, err := d.Start(ctx.ExecCtx, task)
   292  	if err == nil {
   293  		resp.Handle.Kill()
   294  		t.Fatalf("Should've failed")
   295  	}
   296  	msg := "unknown user alice"
   297  	if !strings.Contains(err.Error(), msg) {
   298  		t.Fatalf("Expecting '%v' in '%v'", msg, err)
   299  	}
   300  }
   301  
   302  func TestRawExecDriver_HandlerExec(t *testing.T) {
   303  	t.Parallel()
   304  	task := &structs.Task{
   305  		Name:   "sleep",
   306  		Driver: "raw_exec",
   307  		Config: map[string]interface{}{
   308  			"command": testtask.Path(),
   309  			"args":    []string{"sleep", "9000"},
   310  		},
   311  		LogConfig: &structs.LogConfig{
   312  			MaxFiles:      10,
   313  			MaxFileSizeMB: 10,
   314  		},
   315  		Resources: basicResources,
   316  	}
   317  	testtask.SetTaskEnv(task)
   318  	ctx := testDriverContexts(t, task)
   319  	defer ctx.AllocDir.Destroy()
   320  	d := NewRawExecDriver(ctx.DriverCtx)
   321  
   322  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   323  		t.Fatalf("prestart err: %v", err)
   324  	}
   325  	resp, err := d.Start(ctx.ExecCtx, task)
   326  	if err != nil {
   327  		t.Fatalf("err: %v", err)
   328  	}
   329  
   330  	// Exec a command that should work
   331  	out, code, err := resp.Handle.Exec(context.TODO(), "/usr/bin/stat", []string{"/tmp"})
   332  	if err != nil {
   333  		t.Fatalf("error exec'ing stat: %v", err)
   334  	}
   335  	if code != 0 {
   336  		t.Fatalf("expected `stat /alloc` to succeed but exit code was: %d", code)
   337  	}
   338  	if expected := 100; len(out) < expected {
   339  		t.Fatalf("expected at least %d bytes of output but found %d:\n%s", expected, len(out), out)
   340  	}
   341  
   342  	// Exec a command that should fail
   343  	out, code, err = resp.Handle.Exec(context.TODO(), "/usr/bin/stat", []string{"lkjhdsaflkjshowaisxmcvnlia"})
   344  	if err != nil {
   345  		t.Fatalf("error exec'ing stat: %v", err)
   346  	}
   347  	if code == 0 {
   348  		t.Fatalf("expected `stat` to fail but exit code was: %d", code)
   349  	}
   350  	if expected := "No such file or directory"; !bytes.Contains(out, []byte(expected)) {
   351  		t.Fatalf("expected output to contain %q but found: %q", expected, out)
   352  	}
   353  
   354  	if err := resp.Handle.Kill(); err != nil {
   355  		t.Fatalf("error killing exec handle: %v", err)
   356  	}
   357  }