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