github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/client/driver/java_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"os"
     5  	"os/exec"
     6  	"path/filepath"
     7  	"runtime"
     8  	"strings"
     9  	"syscall"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/hashicorp/nomad/client/config"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  	"github.com/hashicorp/nomad/testutil"
    16  	"github.com/stretchr/testify/assert"
    17  
    18  	ctestutils "github.com/hashicorp/nomad/client/testutil"
    19  )
    20  
    21  var (
    22  	osJavaDriverSupport = map[string]bool{
    23  		"linux": true,
    24  	}
    25  )
    26  
    27  // javaLocated checks whether java is installed so we can run java stuff.
    28  func javaLocated() bool {
    29  	_, err := exec.Command("java", "-version").CombinedOutput()
    30  	return err == nil
    31  }
    32  
    33  // The fingerprinter test should always pass, even if Java is not installed.
    34  func TestJavaDriver_Fingerprint(t *testing.T) {
    35  	if !testutil.IsTravis() {
    36  		t.Parallel()
    37  	}
    38  	ctestutils.JavaCompatible(t)
    39  	task := &structs.Task{
    40  		Name:      "foo",
    41  		Driver:    "java",
    42  		Resources: structs.DefaultResources(),
    43  	}
    44  	ctx := testDriverContexts(t, task)
    45  	defer ctx.AllocDir.Destroy()
    46  	d := NewJavaDriver(ctx.DriverCtx)
    47  	node := &structs.Node{
    48  		Attributes: map[string]string{
    49  			"unique.cgroup.mountpoint": "/sys/fs/cgroups",
    50  		},
    51  	}
    52  	apply, err := d.Fingerprint(&config.Config{}, node)
    53  	if err != nil {
    54  		t.Fatalf("err: %v", err)
    55  	}
    56  	if apply != javaLocated() {
    57  		t.Fatalf("Fingerprinter should detect Java when it is installed")
    58  	}
    59  	if node.Attributes["driver.java"] != "1" {
    60  		if v, ok := osJavaDriverSupport[runtime.GOOS]; v && ok {
    61  			t.Fatalf("missing java driver")
    62  		} else {
    63  			t.Skipf("missing java driver, no OS support")
    64  		}
    65  	}
    66  	for _, key := range []string{"driver.java.version", "driver.java.runtime", "driver.java.vm"} {
    67  		if node.Attributes[key] == "" {
    68  			t.Fatalf("missing driver key (%s)", key)
    69  		}
    70  	}
    71  }
    72  
    73  func TestJavaDriver_StartOpen_Wait(t *testing.T) {
    74  	if !testutil.IsTravis() {
    75  		t.Parallel()
    76  	}
    77  	if !javaLocated() {
    78  		t.Skip("Java not found; skipping")
    79  	}
    80  
    81  	ctestutils.JavaCompatible(t)
    82  	task := &structs.Task{
    83  		Name:   "demo-app",
    84  		Driver: "java",
    85  		Config: map[string]interface{}{
    86  			"jar_path":    "demoapp.jar",
    87  			"jvm_options": []string{"-Xmx64m", "-Xms32m"},
    88  		},
    89  		LogConfig: &structs.LogConfig{
    90  			MaxFiles:      10,
    91  			MaxFileSizeMB: 10,
    92  		},
    93  		Resources: basicResources,
    94  	}
    95  
    96  	ctx := testDriverContexts(t, task)
    97  	defer ctx.AllocDir.Destroy()
    98  	d := NewJavaDriver(ctx.DriverCtx)
    99  
   100  	// Copy the test jar into the task's directory
   101  	dst := ctx.ExecCtx.TaskDir.Dir
   102  	copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t)
   103  
   104  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   105  		t.Fatalf("prestart err: %v", err)
   106  	}
   107  	resp, err := d.Start(ctx.ExecCtx, task)
   108  	if err != nil {
   109  		t.Fatalf("err: %v", err)
   110  	}
   111  
   112  	// Attempt to open
   113  	handle2, err := d.Open(ctx.ExecCtx, resp.Handle.ID())
   114  	if err != nil {
   115  		t.Fatalf("err: %v", err)
   116  	}
   117  	if handle2 == nil {
   118  		t.Fatalf("missing handle")
   119  	}
   120  
   121  	time.Sleep(2 * time.Second)
   122  
   123  	// There is a race condition between the handle waiting and killing. One
   124  	// will return an error.
   125  	resp.Handle.Kill()
   126  	handle2.Kill()
   127  }
   128  
   129  func TestJavaDriver_Start_Wait(t *testing.T) {
   130  	if !testutil.IsTravis() {
   131  		t.Parallel()
   132  	}
   133  	if !javaLocated() {
   134  		t.Skip("Java not found; skipping")
   135  	}
   136  
   137  	ctestutils.JavaCompatible(t)
   138  	task := &structs.Task{
   139  		Name:   "demo-app",
   140  		Driver: "java",
   141  		Config: map[string]interface{}{
   142  			"jar_path": "demoapp.jar",
   143  			"args":     []string{"1"},
   144  		},
   145  		LogConfig: &structs.LogConfig{
   146  			MaxFiles:      10,
   147  			MaxFileSizeMB: 10,
   148  		},
   149  		Resources: basicResources,
   150  	}
   151  
   152  	ctx := testDriverContexts(t, task)
   153  	defer ctx.AllocDir.Destroy()
   154  	d := NewJavaDriver(ctx.DriverCtx)
   155  
   156  	// Copy the test jar into the task's directory
   157  	dst := ctx.ExecCtx.TaskDir.Dir
   158  	copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t)
   159  
   160  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   161  		t.Fatalf("prestart err: %v", err)
   162  	}
   163  	resp, err := d.Start(ctx.ExecCtx, task)
   164  	if err != nil {
   165  		t.Fatalf("err: %v", err)
   166  	}
   167  
   168  	// Task should terminate after 1 seconds
   169  	select {
   170  	case res := <-resp.Handle.WaitCh():
   171  		if !res.Successful() {
   172  			t.Fatalf("err: %v", res.String())
   173  		}
   174  	case <-time.After(5 * time.Second):
   175  		t.Fatalf("timeout")
   176  	}
   177  
   178  	// Get the stdout of the process and assrt that it's not empty
   179  	stdout := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "demo-app.stdout.0")
   180  	fInfo, err := os.Stat(stdout)
   181  	if err != nil {
   182  		t.Fatalf("failed to get stdout of process: %v", err)
   183  	}
   184  	if fInfo.Size() == 0 {
   185  		t.Fatalf("stdout of process is empty")
   186  	}
   187  
   188  	// need to kill long lived process
   189  	err = resp.Handle.Kill()
   190  	if err != nil {
   191  		t.Fatalf("Error: %s", err)
   192  	}
   193  }
   194  
   195  func TestJavaDriver_Start_Kill_Wait(t *testing.T) {
   196  	if !testutil.IsTravis() {
   197  		t.Parallel()
   198  	}
   199  	if !javaLocated() {
   200  		t.Skip("Java not found; skipping")
   201  	}
   202  
   203  	ctestutils.JavaCompatible(t)
   204  	task := &structs.Task{
   205  		Name:   "demo-app",
   206  		Driver: "java",
   207  		Config: map[string]interface{}{
   208  			"jar_path": "demoapp.jar",
   209  			"args":     []string{"5"},
   210  		},
   211  		LogConfig: &structs.LogConfig{
   212  			MaxFiles:      10,
   213  			MaxFileSizeMB: 10,
   214  		},
   215  		Resources: basicResources,
   216  	}
   217  
   218  	ctx := testDriverContexts(t, task)
   219  	defer ctx.AllocDir.Destroy()
   220  	d := NewJavaDriver(ctx.DriverCtx)
   221  
   222  	// Copy the test jar into the task's directory
   223  	dst := ctx.ExecCtx.TaskDir.Dir
   224  	copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t)
   225  
   226  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   227  		t.Fatalf("prestart err: %v", err)
   228  	}
   229  	resp, err := d.Start(ctx.ExecCtx, task)
   230  	if err != nil {
   231  		t.Fatalf("err: %v", err)
   232  	}
   233  
   234  	errCh := make(chan error, 1)
   235  	go func() {
   236  		time.Sleep(10 * time.Millisecond)
   237  		err := resp.Handle.Kill()
   238  		if err != nil {
   239  			errCh <- err
   240  		}
   241  	}()
   242  
   243  	// Task should terminate quickly
   244  	select {
   245  	case err := <-errCh:
   246  		t.Fatalf("err: %v", err)
   247  	case res := <-resp.Handle.WaitCh():
   248  		if res.Successful() {
   249  			t.Fatal("should err")
   250  		}
   251  	case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second):
   252  		t.Fatalf("timeout")
   253  
   254  		// Need to kill long lived process
   255  		if err = resp.Handle.Kill(); err != nil {
   256  			t.Fatalf("Error: %s", err)
   257  		}
   258  	}
   259  }
   260  
   261  func TestJavaDriver_Signal(t *testing.T) {
   262  	if !testutil.IsTravis() {
   263  		t.Parallel()
   264  	}
   265  	if !javaLocated() {
   266  		t.Skip("Java not found; skipping")
   267  	}
   268  
   269  	ctestutils.JavaCompatible(t)
   270  	task := &structs.Task{
   271  		Name:   "demo-app",
   272  		Driver: "java",
   273  		Config: map[string]interface{}{
   274  			"jar_path": "demoapp.jar",
   275  			"args":     []string{"5"},
   276  		},
   277  		LogConfig: &structs.LogConfig{
   278  			MaxFiles:      10,
   279  			MaxFileSizeMB: 10,
   280  		},
   281  		Resources: basicResources,
   282  	}
   283  
   284  	ctx := testDriverContexts(t, task)
   285  	defer ctx.AllocDir.Destroy()
   286  	d := NewJavaDriver(ctx.DriverCtx)
   287  
   288  	// Copy the test jar into the task's directory
   289  	dst := ctx.ExecCtx.TaskDir.Dir
   290  	copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t)
   291  
   292  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   293  		t.Fatalf("prestart err: %v", err)
   294  	}
   295  	resp, err := d.Start(ctx.ExecCtx, task)
   296  	if err != nil {
   297  		t.Fatalf("err: %v", err)
   298  	}
   299  
   300  	errCh := make(chan error, 1)
   301  	go func() {
   302  		time.Sleep(10 * time.Millisecond)
   303  		err := resp.Handle.Signal(syscall.SIGHUP)
   304  		if err != nil {
   305  			errCh <- err
   306  		}
   307  	}()
   308  
   309  	// Task should terminate quickly
   310  	select {
   311  	case err := <-errCh:
   312  		t.Fatalf("err: %v", err)
   313  	case res := <-resp.Handle.WaitCh():
   314  		if res.Successful() {
   315  			t.Fatal("should err")
   316  		}
   317  	case <-time.After(time.Duration(testutil.TestMultiplier()*10) * time.Second):
   318  		t.Fatalf("timeout")
   319  
   320  		// Need to kill long lived process
   321  		if err = resp.Handle.Kill(); err != nil {
   322  			t.Fatalf("Error: %s", err)
   323  		}
   324  	}
   325  }
   326  
   327  func TestJavaDriver_User(t *testing.T) {
   328  	if !testutil.IsTravis() {
   329  		t.Parallel()
   330  	}
   331  	if !javaLocated() {
   332  		t.Skip("Java not found; skipping")
   333  	}
   334  	if runtime.GOOS != "linux" {
   335  		t.Skip("Linux only test")
   336  	}
   337  
   338  	ctestutils.JavaCompatible(t)
   339  	task := &structs.Task{
   340  		Name:   "demo-app",
   341  		Driver: "java",
   342  		User:   "alice",
   343  		Config: map[string]interface{}{
   344  			"jar_path": "demoapp.jar",
   345  		},
   346  		LogConfig: &structs.LogConfig{
   347  			MaxFiles:      10,
   348  			MaxFileSizeMB: 10,
   349  		},
   350  		Resources: basicResources,
   351  	}
   352  
   353  	ctx := testDriverContexts(t, task)
   354  	defer ctx.AllocDir.Destroy()
   355  	d := NewJavaDriver(ctx.DriverCtx)
   356  
   357  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   358  		t.Fatalf("prestart err: %v", err)
   359  	}
   360  	resp, err := d.Start(ctx.ExecCtx, task)
   361  	if err == nil {
   362  		resp.Handle.Kill()
   363  		t.Fatalf("Should've failed")
   364  	}
   365  	msg := "user alice"
   366  	if !strings.Contains(err.Error(), msg) {
   367  		t.Fatalf("Expecting '%v' in '%v'", msg, err)
   368  	}
   369  }
   370  
   371  func TestJavaDriver_Start_Wait_Class(t *testing.T) {
   372  	if !testutil.IsTravis() {
   373  		t.Parallel()
   374  	}
   375  	if !javaLocated() {
   376  		t.Skip("Java not found; skipping")
   377  	}
   378  
   379  	ctestutils.JavaCompatible(t)
   380  	task := &structs.Task{
   381  		Name:   "demo-app",
   382  		Driver: "java",
   383  		Config: map[string]interface{}{
   384  			"class_path": "${NOMAD_TASK_DIR}",
   385  			"class":      "Hello",
   386  			"args":       []string{"1"},
   387  		},
   388  		LogConfig: &structs.LogConfig{
   389  			MaxFiles:      10,
   390  			MaxFileSizeMB: 10,
   391  		},
   392  		Resources: basicResources,
   393  	}
   394  
   395  	ctx := testDriverContexts(t, task)
   396  	defer ctx.AllocDir.Destroy()
   397  	d := NewJavaDriver(ctx.DriverCtx)
   398  
   399  	// Copy the test jar into the task's directory
   400  	dst := ctx.ExecCtx.TaskDir.LocalDir
   401  	copyFile("./test-resources/java/Hello.class", filepath.Join(dst, "Hello.class"), t)
   402  
   403  	if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
   404  		t.Fatalf("prestart err: %v", err)
   405  	}
   406  	resp, err := d.Start(ctx.ExecCtx, task)
   407  	if err != nil {
   408  		t.Fatalf("err: %v", err)
   409  	}
   410  
   411  	// Task should terminate after 1 seconds
   412  	select {
   413  	case res := <-resp.Handle.WaitCh():
   414  		if !res.Successful() {
   415  			t.Fatalf("err: %v", res.String())
   416  		}
   417  	case <-time.After(5 * time.Second):
   418  		t.Fatalf("timeout")
   419  	}
   420  
   421  	// Get the stdout of the process and assrt that it's not empty
   422  	stdout := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "demo-app.stdout.0")
   423  	fInfo, err := os.Stat(stdout)
   424  	if err != nil {
   425  		t.Fatalf("failed to get stdout of process: %v", err)
   426  	}
   427  	if fInfo.Size() == 0 {
   428  		t.Fatalf("stdout of process is empty")
   429  	}
   430  
   431  	// need to kill long lived process
   432  	if err := resp.Handle.Kill(); err != nil {
   433  		t.Fatalf("Error: %s", err)
   434  	}
   435  }
   436  
   437  func TestJavaDriver_Start_Kill(t *testing.T) {
   438  	assert := assert.New(t)
   439  
   440  	if !testutil.IsTravis() {
   441  		t.Parallel()
   442  	}
   443  	if !javaLocated() {
   444  		t.Skip("Java not found; skipping")
   445  	}
   446  
   447  	// Test that a valid kill signal will successfully stop the process
   448  	{
   449  		ctestutils.JavaCompatible(t)
   450  		task := &structs.Task{
   451  			Name:       "demo-app",
   452  			Driver:     "java",
   453  			KillSignal: "SIGKILL",
   454  			Config: map[string]interface{}{
   455  				"jar_path": "demoapp.jar",
   456  				"args":     []string{"5"},
   457  			},
   458  			LogConfig: &structs.LogConfig{
   459  				MaxFiles:      10,
   460  				MaxFileSizeMB: 10,
   461  			},
   462  			Resources: basicResources,
   463  		}
   464  
   465  		ctx := testDriverContexts(t, task)
   466  		defer ctx.AllocDir.Destroy()
   467  		d := NewJavaDriver(ctx.DriverCtx)
   468  
   469  		// Copy the test jar into the task's directory
   470  		dst := ctx.ExecCtx.TaskDir.Dir
   471  		copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t)
   472  
   473  		_, err := d.Prestart(ctx.ExecCtx, task)
   474  		assert.Nil(err)
   475  
   476  		resp, err := d.Start(ctx.ExecCtx, task)
   477  		assert.Nil(err)
   478  
   479  		assert.NotNil(resp.Handle)
   480  		err = resp.Handle.Kill()
   481  		assert.Nil(err)
   482  	}
   483  
   484  	// Test that an unsupported kill signal will return an error
   485  	{
   486  		ctestutils.JavaCompatible(t)
   487  		task := &structs.Task{
   488  			Name:       "demo-app",
   489  			Driver:     "java",
   490  			KillSignal: "ABCDEF",
   491  			Config: map[string]interface{}{
   492  				"jar_path": "demoapp.jar",
   493  				"args":     []string{"5"},
   494  			},
   495  			LogConfig: &structs.LogConfig{
   496  				MaxFiles:      10,
   497  				MaxFileSizeMB: 10,
   498  			},
   499  			Resources: basicResources,
   500  		}
   501  
   502  		ctx := testDriverContexts(t, task)
   503  		defer ctx.AllocDir.Destroy()
   504  		d := NewJavaDriver(ctx.DriverCtx)
   505  
   506  		// Copy the test jar into the task's directory
   507  		dst := ctx.ExecCtx.TaskDir.Dir
   508  		copyFile("./test-resources/java/demoapp.jar", filepath.Join(dst, "demoapp.jar"), t)
   509  
   510  		_, err := d.Prestart(ctx.ExecCtx, task)
   511  		assert.Nil(err)
   512  
   513  		_, err = d.Start(ctx.ExecCtx, task)
   514  		assert.NotNil(err)
   515  		assert.Contains(err.Error(), "Signal ABCDEF is not supported")
   516  	}
   517  }