github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/drivers/java/driver_test.go (about)

     1  package java
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils"
    12  
    13  	"context"
    14  	"time"
    15  
    16  	ctestutil "github.com/hashicorp/nomad/client/testutil"
    17  	"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
    18  	"github.com/hashicorp/nomad/helper/testlog"
    19  	"github.com/hashicorp/nomad/helper/uuid"
    20  	"github.com/hashicorp/nomad/nomad/structs"
    21  	"github.com/hashicorp/nomad/plugins/drivers"
    22  	"github.com/hashicorp/nomad/testutil"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func javaCompatible(t *testing.T) {
    27  	ctestutil.JavaCompatible(t)
    28  
    29  	_, _, _, err := javaVersionInfo()
    30  	if err != nil {
    31  		t.Skipf("java not found; skipping: %v", err)
    32  	}
    33  }
    34  
    35  func TestJavaDriver_Fingerprint(t *testing.T) {
    36  	javaCompatible(t)
    37  	if !testutil.IsCI() {
    38  		t.Parallel()
    39  	}
    40  
    41  	d := NewDriver(testlog.HCLogger(t))
    42  	harness := dtestutil.NewDriverHarness(t, d)
    43  
    44  	fpCh, err := harness.Fingerprint(context.Background())
    45  	require.NoError(t, err)
    46  
    47  	select {
    48  	case fp := <-fpCh:
    49  		require.Equal(t, drivers.HealthStateHealthy, fp.Health)
    50  		detected, _ := fp.Attributes["driver.java"].GetBool()
    51  		require.True(t, detected)
    52  	case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
    53  		require.Fail(t, "timeout receiving fingerprint")
    54  	}
    55  }
    56  
    57  func TestJavaDriver_Jar_Start_Wait(t *testing.T) {
    58  	javaCompatible(t)
    59  	if !testutil.IsCI() {
    60  		t.Parallel()
    61  	}
    62  
    63  	require := require.New(t)
    64  	d := NewDriver(testlog.HCLogger(t))
    65  	harness := dtestutil.NewDriverHarness(t, d)
    66  
    67  	tc := &TaskConfig{
    68  		JarPath: "demoapp.jar",
    69  		Args:    []string{"1"},
    70  		JvmOpts: []string{"-Xmx64m", "-Xms32m"},
    71  	}
    72  	task := basicTask(t, "demo-app", tc)
    73  
    74  	cleanup := harness.MkAllocDir(task, true)
    75  	defer cleanup()
    76  
    77  	copyFile("./test-resources/demoapp.jar", filepath.Join(task.TaskDir().Dir, "demoapp.jar"), t)
    78  
    79  	handle, _, err := harness.StartTask(task)
    80  	require.NoError(err)
    81  
    82  	ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
    83  	require.NoError(err)
    84  	result := <-ch
    85  	require.Nil(result.Err)
    86  
    87  	require.Zero(result.ExitCode)
    88  
    89  	// Get the stdout of the process and assert that it's not empty
    90  	stdout, err := ioutil.ReadFile(filepath.Join(task.TaskDir().LogDir, "demo-app.stdout.0"))
    91  	require.NoError(err)
    92  	require.Contains(string(stdout), "Hello")
    93  
    94  	require.NoError(harness.DestroyTask(task.ID, true))
    95  }
    96  
    97  func TestJavaDriver_Jar_Stop_Wait(t *testing.T) {
    98  	javaCompatible(t)
    99  	if !testutil.IsCI() {
   100  		t.Parallel()
   101  	}
   102  
   103  	require := require.New(t)
   104  	d := NewDriver(testlog.HCLogger(t))
   105  	harness := dtestutil.NewDriverHarness(t, d)
   106  
   107  	tc := &TaskConfig{
   108  		JarPath: "demoapp.jar",
   109  		Args:    []string{"600"},
   110  		JvmOpts: []string{"-Xmx64m", "-Xms32m"},
   111  	}
   112  	task := basicTask(t, "demo-app", tc)
   113  
   114  	cleanup := harness.MkAllocDir(task, true)
   115  	defer cleanup()
   116  
   117  	copyFile("./test-resources/demoapp.jar", filepath.Join(task.TaskDir().Dir, "demoapp.jar"), t)
   118  
   119  	handle, _, err := harness.StartTask(task)
   120  	require.NoError(err)
   121  
   122  	ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
   123  	require.NoError(err)
   124  
   125  	require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second))
   126  
   127  	go func() {
   128  		time.Sleep(10 * time.Millisecond)
   129  		harness.StopTask(task.ID, 2*time.Second, "SIGINT")
   130  	}()
   131  
   132  	select {
   133  	case result := <-ch:
   134  		require.False(result.Successful())
   135  	case <-time.After(10 * time.Second):
   136  		require.Fail("timeout waiting for task to shutdown")
   137  	}
   138  
   139  	// Ensure that the task is marked as dead, but account
   140  	// for WaitTask() closing channel before internal state is updated
   141  	testutil.WaitForResult(func() (bool, error) {
   142  		status, err := harness.InspectTask(task.ID)
   143  		if err != nil {
   144  			return false, fmt.Errorf("inspecting task failed: %v", err)
   145  		}
   146  		if status.State != drivers.TaskStateExited {
   147  			return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State)
   148  		}
   149  
   150  		return true, nil
   151  	}, func(err error) {
   152  		require.NoError(err)
   153  	})
   154  
   155  	require.NoError(harness.DestroyTask(task.ID, true))
   156  }
   157  
   158  func TestJavaDriver_Class_Start_Wait(t *testing.T) {
   159  	javaCompatible(t)
   160  	if !testutil.IsCI() {
   161  		t.Parallel()
   162  	}
   163  
   164  	require := require.New(t)
   165  	d := NewDriver(testlog.HCLogger(t))
   166  	harness := dtestutil.NewDriverHarness(t, d)
   167  
   168  	tc := &TaskConfig{
   169  		Class: "Hello",
   170  		Args:  []string{"1"},
   171  	}
   172  	task := basicTask(t, "demo-app", tc)
   173  
   174  	cleanup := harness.MkAllocDir(task, true)
   175  	defer cleanup()
   176  
   177  	copyFile("./test-resources/Hello.class", filepath.Join(task.TaskDir().Dir, "Hello.class"), t)
   178  
   179  	handle, _, err := harness.StartTask(task)
   180  	require.NoError(err)
   181  
   182  	ch, err := harness.WaitTask(context.Background(), handle.Config.ID)
   183  	require.NoError(err)
   184  	result := <-ch
   185  	require.Nil(result.Err)
   186  
   187  	require.Zero(result.ExitCode)
   188  
   189  	// Get the stdout of the process and assert that it's not empty
   190  	stdout, err := ioutil.ReadFile(filepath.Join(task.TaskDir().LogDir, "demo-app.stdout.0"))
   191  	require.NoError(err)
   192  	require.Contains(string(stdout), "Hello")
   193  
   194  	require.NoError(harness.DestroyTask(task.ID, true))
   195  }
   196  
   197  func TestJavaCmdArgs(t *testing.T) {
   198  	cases := []struct {
   199  		name     string
   200  		cfg      TaskConfig
   201  		expected []string
   202  	}{
   203  		{
   204  			"jar_path_full",
   205  			TaskConfig{
   206  				JvmOpts: []string{"-Xmx512m", "-Xms128m"},
   207  				JarPath: "/jar-path.jar",
   208  				Args:    []string{"hello", "world"},
   209  			},
   210  			[]string{"-Xmx512m", "-Xms128m", "-jar", "/jar-path.jar", "hello", "world"},
   211  		},
   212  		{
   213  			"class_full",
   214  			TaskConfig{
   215  				JvmOpts:   []string{"-Xmx512m", "-Xms128m"},
   216  				Class:     "ClassName",
   217  				ClassPath: "/classpath",
   218  				Args:      []string{"hello", "world"},
   219  			},
   220  			[]string{"-Xmx512m", "-Xms128m", "-cp", "/classpath", "ClassName", "hello", "world"},
   221  		},
   222  		{
   223  			"jar_path_slim",
   224  			TaskConfig{
   225  				JarPath: "/jar-path.jar",
   226  			},
   227  			[]string{"-jar", "/jar-path.jar"},
   228  		},
   229  		{
   230  			"class_slim",
   231  			TaskConfig{
   232  				Class: "ClassName",
   233  			},
   234  			[]string{"ClassName"},
   235  		},
   236  	}
   237  
   238  	for _, c := range cases {
   239  		t.Run(c.name, func(t *testing.T) {
   240  			found := javaCmdArgs(c.cfg)
   241  			require.Equal(t, c.expected, found)
   242  		})
   243  	}
   244  }
   245  
   246  func TestJavaDriver_ExecTaskStreaming(t *testing.T) {
   247  	javaCompatible(t)
   248  	if !testutil.IsCI() {
   249  		t.Parallel()
   250  	}
   251  
   252  	require := require.New(t)
   253  	d := NewDriver(testlog.HCLogger(t))
   254  	harness := dtestutil.NewDriverHarness(t, d)
   255  	defer harness.Kill()
   256  
   257  	tc := &TaskConfig{
   258  		Class: "Hello",
   259  		Args:  []string{"900"},
   260  	}
   261  	task := basicTask(t, "demo-app", tc)
   262  
   263  	cleanup := harness.MkAllocDir(task, true)
   264  	defer cleanup()
   265  
   266  	copyFile("./test-resources/Hello.class", filepath.Join(task.TaskDir().Dir, "Hello.class"), t)
   267  
   268  	_, _, err := harness.StartTask(task)
   269  	require.NoError(err)
   270  	defer d.DestroyTask(task.ID, true)
   271  
   272  	dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID)
   273  
   274  }
   275  func basicTask(t *testing.T, name string, taskConfig *TaskConfig) *drivers.TaskConfig {
   276  	t.Helper()
   277  
   278  	task := &drivers.TaskConfig{
   279  		ID:   uuid.Generate(),
   280  		Name: name,
   281  		Resources: &drivers.Resources{
   282  			NomadResources: &structs.AllocatedTaskResources{
   283  				Memory: structs.AllocatedMemoryResources{
   284  					MemoryMB: 128,
   285  				},
   286  				Cpu: structs.AllocatedCpuResources{
   287  					CpuShares: 100,
   288  				},
   289  			},
   290  			LinuxResources: &drivers.LinuxResources{
   291  				MemoryLimitBytes: 134217728,
   292  				CPUShares:        100,
   293  			},
   294  		},
   295  	}
   296  
   297  	require.NoError(t, task.EncodeConcreteDriverConfig(&taskConfig))
   298  	return task
   299  }
   300  
   301  // copyFile moves an existing file to the destination
   302  func copyFile(src, dst string, t *testing.T) {
   303  	in, err := os.Open(src)
   304  	if err != nil {
   305  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
   306  	}
   307  	defer in.Close()
   308  	out, err := os.Create(dst)
   309  	if err != nil {
   310  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
   311  	}
   312  	defer func() {
   313  		if err := out.Close(); err != nil {
   314  			t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
   315  		}
   316  	}()
   317  	if _, err = io.Copy(out, in); err != nil {
   318  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
   319  	}
   320  	if err := out.Sync(); err != nil {
   321  		t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
   322  	}
   323  }
   324  
   325  func TestConfig_ParseAllHCL(t *testing.T) {
   326  	cfgStr := `
   327  config {
   328    class = "java.main"
   329    class_path = "/tmp/cp"
   330    jar_path = "/tmp/jar.jar"
   331    jvm_options = ["-Xmx600"]
   332    args = ["arg1", "arg2"]
   333  }`
   334  
   335  	expected := &TaskConfig{
   336  		Class:     "java.main",
   337  		ClassPath: "/tmp/cp",
   338  		JarPath:   "/tmp/jar.jar",
   339  		JvmOpts:   []string{"-Xmx600"},
   340  		Args:      []string{"arg1", "arg2"},
   341  	}
   342  
   343  	var tc *TaskConfig
   344  	hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
   345  
   346  	require.EqualValues(t, expected, tc)
   347  }