github.com/anuvu/nomad@v0.8.7-atom1/client/driver/raw_exec_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"strconv"
    12  	"syscall"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/hashicorp/nomad/client/config"
    17  	"github.com/hashicorp/nomad/client/driver/env"
    18  	cstructs "github.com/hashicorp/nomad/client/structs"
    19  	tu "github.com/hashicorp/nomad/client/testutil"
    20  	"github.com/hashicorp/nomad/helper/testtask"
    21  	"github.com/hashicorp/nomad/nomad/structs"
    22  	"github.com/hashicorp/nomad/testutil"
    23  )
    24  
    25  func TestRawExecDriver_Fingerprint(t *testing.T) {
    26  	t.Parallel()
    27  	task := &structs.Task{
    28  		Name:      "foo",
    29  		Driver:    "raw_exec",
    30  		Resources: structs.DefaultResources(),
    31  	}
    32  	ctx := testDriverContexts(t, task)
    33  	defer ctx.AllocDir.Destroy()
    34  	d := NewRawExecDriver(ctx.DriverCtx)
    35  	node := &structs.Node{
    36  		Attributes: make(map[string]string),
    37  	}
    38  
    39  	// Disable raw exec.
    40  	cfg := &config.Config{Options: map[string]string{rawExecEnableOption: "false"}}
    41  
    42  	request := &cstructs.FingerprintRequest{Config: cfg, Node: node}
    43  	var response cstructs.FingerprintResponse
    44  	err := d.Fingerprint(request, &response)
    45  	if err != nil {
    46  		t.Fatalf("err: %v", err)
    47  	}
    48  
    49  	if response.Attributes["driver.raw_exec"] != "" {
    50  		t.Fatalf("driver incorrectly enabled")
    51  	}
    52  
    53  	// Enable raw exec.
    54  	request.Config.Options[rawExecEnableOption] = "true"
    55  	err = d.Fingerprint(request, &response)
    56  	if err != nil {
    57  		t.Fatalf("err: %v", err)
    58  	}
    59  
    60  	if !response.Detected {
    61  		t.Fatalf("expected response to be applicable")
    62  	}
    63  
    64  	if response.Attributes["driver.raw_exec"] != "1" {
    65  		t.Fatalf("driver not enabled")
    66  	}
    67  }
    68  
    69  func TestRawExecDriver_StartOpen_Wait(t *testing.T) {
    70  	t.Parallel()
    71  	task := &structs.Task{
    72  		Name:   "sleep",
    73  		Driver: "raw_exec",
    74  		Config: map[string]interface{}{
    75  			"command": testtask.Path(),
    76  			"args":    []string{"sleep", "1s"},
    77  		},
    78  		LogConfig: &structs.LogConfig{
    79  			MaxFiles:      10,
    80  			MaxFileSizeMB: 10,
    81  		},
    82  		Resources: basicResources,
    83  	}
    84  	testtask.SetTaskEnv(task)
    85  	ctx := testDriverContexts(t, task)
    86  	defer ctx.AllocDir.Destroy()
    87  	d := NewRawExecDriver(ctx.DriverCtx)
    88  
    89  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
    90  		t.Fatalf("prestart err: %v", err)
    91  	}
    92  	resp, err := d.Start(ctx.ExecCtx, task)
    93  	if err != nil {
    94  		t.Fatalf("err: %v", err)
    95  	}
    96  
    97  	// Attempt to open
    98  	handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID())
    99  	if err != nil {
   100  		t.Fatalf("err: %v", err)
   101  	}
   102  	if handle2 == nil {
   103  		t.Fatalf("missing handle")
   104  	}
   105  
   106  	// Task should terminate quickly
   107  	select {
   108  	case <-handle2.WaitCh():
   109  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   110  		t.Fatalf("timeout")
   111  	}
   112  	resp.Handle.Kill()
   113  	handle2.Kill()
   114  }
   115  
   116  func TestRawExecDriver_Start_Wait(t *testing.T) {
   117  	t.Parallel()
   118  	task := &structs.Task{
   119  		Name:   "sleep",
   120  		Driver: "raw_exec",
   121  		Config: map[string]interface{}{
   122  			"command": testtask.Path(),
   123  			"args":    []string{"sleep", "1s"},
   124  		},
   125  		LogConfig: &structs.LogConfig{
   126  			MaxFiles:      10,
   127  			MaxFileSizeMB: 10,
   128  		},
   129  		Resources: basicResources,
   130  	}
   131  	testtask.SetTaskEnv(task)
   132  	ctx := testDriverContexts(t, task)
   133  	defer ctx.AllocDir.Destroy()
   134  	d := NewRawExecDriver(ctx.DriverCtx)
   135  
   136  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   137  		t.Fatalf("prestart err: %v", err)
   138  	}
   139  	resp, err := d.Start(ctx.ExecCtx, task)
   140  	if err != nil {
   141  		t.Fatalf("err: %v", err)
   142  	}
   143  
   144  	// Update should be a no-op
   145  	err = resp.Handle.Update(task)
   146  	if err != nil {
   147  		t.Fatalf("err: %v", err)
   148  	}
   149  
   150  	// Task should terminate quickly
   151  	select {
   152  	case res := <-resp.Handle.WaitCh():
   153  		if !res.Successful() {
   154  			t.Fatalf("err: %v", res)
   155  		}
   156  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   157  		t.Fatalf("timeout")
   158  	}
   159  }
   160  
   161  func TestRawExecDriver_Start_Wait_AllocDir(t *testing.T) {
   162  	t.Parallel()
   163  	exp := []byte("win")
   164  	file := "output.txt"
   165  	outPath := fmt.Sprintf(`${%s}/%s`, env.AllocDir, file)
   166  	task := &structs.Task{
   167  		Name:   "sleep",
   168  		Driver: "raw_exec",
   169  		Config: map[string]interface{}{
   170  			"command": testtask.Path(),
   171  			"args": []string{
   172  				"sleep", "1s",
   173  				"write", string(exp), outPath,
   174  			},
   175  		},
   176  		LogConfig: &structs.LogConfig{
   177  			MaxFiles:      10,
   178  			MaxFileSizeMB: 10,
   179  		},
   180  		Resources: basicResources,
   181  	}
   182  	testtask.SetTaskEnv(task)
   183  
   184  	ctx := testDriverContexts(t, task)
   185  	defer ctx.AllocDir.Destroy()
   186  	d := NewRawExecDriver(ctx.DriverCtx)
   187  
   188  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   189  		t.Fatalf("prestart err: %v", err)
   190  	}
   191  	resp, err := d.Start(ctx.ExecCtx, task)
   192  	if err != nil {
   193  		t.Fatalf("err: %v", err)
   194  	}
   195  
   196  	// Task should terminate quickly
   197  	select {
   198  	case res := <-resp.Handle.WaitCh():
   199  		if !res.Successful() {
   200  			t.Fatalf("err: %v", res)
   201  		}
   202  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   203  		t.Fatalf("timeout")
   204  	}
   205  
   206  	// Check that data was written to the shared alloc directory.
   207  	outputFile := filepath.Join(ctx.AllocDir.SharedDir, file)
   208  	act, err := ioutil.ReadFile(outputFile)
   209  	if err != nil {
   210  		t.Fatalf("Couldn't read expected output: %v", err)
   211  	}
   212  
   213  	if !reflect.DeepEqual(act, exp) {
   214  		t.Fatalf("Command outputted %v; want %v", act, exp)
   215  	}
   216  }
   217  
   218  func TestRawExecDriver_Start_Kill_Wait(t *testing.T) {
   219  	t.Parallel()
   220  	task := &structs.Task{
   221  		Name:   "sleep",
   222  		Driver: "raw_exec",
   223  		Config: map[string]interface{}{
   224  			"command": testtask.Path(),
   225  			"args":    []string{"sleep", "45s"},
   226  		},
   227  		LogConfig: &structs.LogConfig{
   228  			MaxFiles:      10,
   229  			MaxFileSizeMB: 10,
   230  		},
   231  		Resources: basicResources,
   232  	}
   233  	testtask.SetTaskEnv(task)
   234  
   235  	ctx := testDriverContexts(t, task)
   236  	defer ctx.AllocDir.Destroy()
   237  	d := NewRawExecDriver(ctx.DriverCtx)
   238  
   239  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   240  		t.Fatalf("prestart err: %v", err)
   241  	}
   242  	resp, err := d.Start(ctx.ExecCtx, task)
   243  	if err != nil {
   244  		t.Fatalf("err: %v", err)
   245  	}
   246  
   247  	go func() {
   248  		time.Sleep(1 * time.Second)
   249  		err := resp.Handle.Kill()
   250  
   251  		// Can't rely on the ordering between wait and kill on travis...
   252  		if !testutil.IsTravis() && err != nil {
   253  			t.Fatalf("err: %v", err)
   254  		}
   255  	}()
   256  
   257  	// Task should terminate quickly
   258  	select {
   259  	case res := <-resp.Handle.WaitCh():
   260  		if res.Successful() {
   261  			t.Fatal("should err")
   262  		}
   263  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   264  		t.Fatalf("timeout")
   265  	}
   266  }
   267  
   268  // This test creates a process tree such that without cgroups tracking the
   269  // processes cleanup of the children would not be possible. Thus the test
   270  // asserts that the processes get killed properly when using cgroups.
   271  func TestRawExecDriver_Start_Kill_Wait_Cgroup(t *testing.T) {
   272  	tu.ExecCompatible(t)
   273  	t.Parallel()
   274  	pidFile := "pid"
   275  	task := &structs.Task{
   276  		Name:   "sleep",
   277  		Driver: "raw_exec",
   278  		Config: map[string]interface{}{
   279  			"command": testtask.Path(),
   280  			"args":    []string{"fork/exec", pidFile, "pgrp", "0", "sleep", "20s"},
   281  		},
   282  		LogConfig: &structs.LogConfig{
   283  			MaxFiles:      10,
   284  			MaxFileSizeMB: 10,
   285  		},
   286  		Resources: basicResources,
   287  		User:      "root",
   288  	}
   289  	testtask.SetTaskEnv(task)
   290  
   291  	ctx := testDriverContexts(t, task)
   292  	ctx.DriverCtx.node.Attributes["unique.cgroup.mountpoint"] = "foo" // Enable cgroups
   293  	defer ctx.AllocDir.Destroy()
   294  	d := NewRawExecDriver(ctx.DriverCtx)
   295  
   296  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   297  		t.Fatalf("prestart err: %v", err)
   298  	}
   299  	resp, err := d.Start(ctx.ExecCtx, task)
   300  	if err != nil {
   301  		t.Fatalf("err: %v", err)
   302  	}
   303  
   304  	// Find the process
   305  	var pidData []byte
   306  	testutil.WaitForResult(func() (bool, error) {
   307  		var err error
   308  		pidData, err = ioutil.ReadFile(filepath.Join(ctx.AllocDir.AllocDir, "sleep", pidFile))
   309  		if err != nil {
   310  			return false, err
   311  		}
   312  
   313  		return true, nil
   314  	}, func(err error) {
   315  		t.Fatalf("err: %v", err)
   316  	})
   317  
   318  	pid, err := strconv.Atoi(string(pidData))
   319  	if err != nil {
   320  		t.Fatalf("failed to convert pid: %v", err)
   321  	}
   322  
   323  	// Check the pid is up
   324  	process, err := os.FindProcess(pid)
   325  	if err != nil {
   326  		t.Fatalf("failed to find process")
   327  	}
   328  	if err := process.Signal(syscall.Signal(0)); err != nil {
   329  		t.Fatalf("process doesn't exist: %v", err)
   330  	}
   331  
   332  	go func() {
   333  		time.Sleep(1 * time.Second)
   334  		err := resp.Handle.Kill()
   335  
   336  		// Can't rely on the ordering between wait and kill on travis...
   337  		if !testutil.IsTravis() && err != nil {
   338  			t.Fatalf("err: %v", err)
   339  		}
   340  	}()
   341  
   342  	// Task should terminate quickly
   343  	select {
   344  	case res := <-resp.Handle.WaitCh():
   345  		if res.Successful() {
   346  			t.Fatal("should err")
   347  		}
   348  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
   349  		t.Fatalf("timeout")
   350  	}
   351  
   352  	testutil.WaitForResult(func() (bool, error) {
   353  		if err := process.Signal(syscall.Signal(0)); err == nil {
   354  			return false, fmt.Errorf("process should not exist: %v", pid)
   355  		}
   356  
   357  		return true, nil
   358  	}, func(err error) {
   359  		t.Fatalf("err: %v", err)
   360  	})
   361  }
   362  
   363  func TestRawExecDriver_HandlerExec(t *testing.T) {
   364  	t.Parallel()
   365  	task := &structs.Task{
   366  		Name:   "sleep",
   367  		Driver: "raw_exec",
   368  		Config: map[string]interface{}{
   369  			"command": testtask.Path(),
   370  			"args":    []string{"sleep", "9000s"},
   371  		},
   372  		LogConfig: &structs.LogConfig{
   373  			MaxFiles:      10,
   374  			MaxFileSizeMB: 10,
   375  		},
   376  		Resources: basicResources,
   377  	}
   378  	testtask.SetTaskEnv(task)
   379  	ctx := testDriverContexts(t, task)
   380  	defer ctx.AllocDir.Destroy()
   381  	d := NewRawExecDriver(ctx.DriverCtx)
   382  
   383  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   384  		t.Fatalf("prestart err: %v", err)
   385  	}
   386  	resp, err := d.Start(ctx.ExecCtx, task)
   387  	if err != nil {
   388  		t.Fatalf("err: %v", err)
   389  	}
   390  
   391  	// Exec a command that should work
   392  	out, code, err := resp.Handle.Exec(context.TODO(), "/usr/bin/stat", []string{"/tmp"})
   393  	if err != nil {
   394  		t.Fatalf("error exec'ing stat: %v", err)
   395  	}
   396  	if code != 0 {
   397  		t.Fatalf("expected `stat /alloc` to succeed but exit code was: %d", code)
   398  	}
   399  	if expected := 100; len(out) < expected {
   400  		t.Fatalf("expected at least %d bytes of output but found %d:\n%s", expected, len(out), out)
   401  	}
   402  
   403  	// Exec a command that should fail
   404  	out, code, err = resp.Handle.Exec(context.TODO(), "/usr/bin/stat", []string{"lkjhdsaflkjshowaisxmcvnlia"})
   405  	if err != nil {
   406  		t.Fatalf("error exec'ing stat: %v", err)
   407  	}
   408  	if code == 0 {
   409  		t.Fatalf("expected `stat` to fail but exit code was: %d", code)
   410  	}
   411  	if expected := "No such file or directory"; !bytes.Contains(out, []byte(expected)) {
   412  		t.Fatalf("expected output to contain %q but found: %q", expected, out)
   413  	}
   414  
   415  	select {
   416  	case res := <-resp.Handle.WaitCh():
   417  		t.Fatalf("Shouldn't be exited: %v", res.String())
   418  	default:
   419  	}
   420  
   421  	if err := resp.Handle.Kill(); err != nil {
   422  		t.Fatalf("error killing exec handle: %v", err)
   423  	}
   424  }