From 9206446a8d5f3693e39d6af0bbaa61472929390e Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Fri, 13 Jan 2017 16:03:11 -0800 Subject: [PATCH] Support setting class_path and class name. This PR enhances the java driver to allow setting the class path and class name to run. It also fixes an issue that would make the Java driver attempt to chroot regardless of operating system (this never effected a released version of Nomad). --- client/driver/java.go | 66 +++++++++++++---- client/driver/java_linux.go | 7 ++ client/driver/java_test.go | 67 ++++++++++++++++++ client/driver/java_universal.go | 9 +++ client/driver/test-resources/java/Hello.class | Bin 0 -> 638 bytes .../java/{demoapp.java => Hello.java} | 2 +- website/source/docs/drivers/java.html.md | 33 ++++++++- 7 files changed, 168 insertions(+), 16 deletions(-) create mode 100644 client/driver/java_linux.go create mode 100644 client/driver/java_universal.go create mode 100644 client/driver/test-resources/java/Hello.class rename client/driver/test-resources/java/{demoapp.java => Hello.java} (88%) diff --git a/client/driver/java.go b/client/driver/java.go index 4519a719aac9..366b891e8398 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -18,6 +18,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/hashicorp/nomad/client/config" + "github.com/hashicorp/nomad/client/driver/env" "github.com/hashicorp/nomad/client/driver/executor" dstructs "github.com/hashicorp/nomad/client/driver/structs" "github.com/hashicorp/nomad/client/fingerprint" @@ -40,9 +41,11 @@ type JavaDriver struct { } type JavaDriverConfig struct { - JarPath string `mapstructure:"jar_path"` - JvmOpts []string `mapstructure:"jvm_options"` - Args []string `mapstructure:"args"` + Class string `mapstructure:"class"` + ClassPath string `mapstructure:"class_path"` + JarPath string `mapstructure:"jar_path"` + JvmOpts []string `mapstructure:"jvm_options"` + Args []string `mapstructure:"args"` } // javaHandle is returned from Start/Open as a handle to the PID @@ -70,9 +73,14 @@ func (d *JavaDriver) Validate(config map[string]interface{}) error { fd := &fields.FieldData{ Raw: config, Schema: map[string]*fields.FieldSchema{ + "class": &fields.FieldSchema{ + Type: fields.TypeString, + }, + "class_path": &fields.FieldSchema{ + Type: fields.TypeString, + }, "jar_path": &fields.FieldSchema{ - Type: fields.TypeString, - Required: true, + Type: fields.TypeString, }, "jvm_options": &fields.FieldSchema{ Type: fields.TypeArray, @@ -96,10 +104,6 @@ func (d *JavaDriver) Abilities() DriverAbilities { } } -func (d *JavaDriver) FSIsolation() cstructs.FSIsolation { - return cstructs.FSIsolationChroot -} - func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // Get the current status so that we can log any debug messages only if the // state changes @@ -167,25 +171,59 @@ func (d *JavaDriver) Prestart(ctx *ExecContext, task *structs.Task) error { return nil } -func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { +func NewJavaDriverConfig(task *structs.Task, env *env.TaskEnvironment) (*JavaDriverConfig, error) { var driverConfig JavaDriverConfig if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } - if driverConfig.JarPath == "" { - return nil, fmt.Errorf("jar_path must be specified") + // Interpolate everything + driverConfig.Class = env.ReplaceEnv(driverConfig.Class) + driverConfig.ClassPath = env.ReplaceEnv(driverConfig.ClassPath) + driverConfig.JarPath = env.ReplaceEnv(driverConfig.JarPath) + driverConfig.JvmOpts = env.ParseAndReplace(driverConfig.JvmOpts) + driverConfig.Args = env.ParseAndReplace(driverConfig.Args) + + // Validate + jarSpecified := driverConfig.JarPath != "" + classSpecified := driverConfig.Class != "" + if !jarSpecified && !classSpecified { + return nil, fmt.Errorf("jar_path or class must be specified") + } + + return &driverConfig, nil +} + +func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { + driverConfig, err := NewJavaDriverConfig(task, d.taskEnv) + if err != nil { + return nil, err } args := []string{} + // Look for jvm options if len(driverConfig.JvmOpts) != 0 { d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", driverConfig.JvmOpts) args = append(args, driverConfig.JvmOpts...) } - // Build the argument list. - args = append(args, "-jar", driverConfig.JarPath) + // Add the classpath + if driverConfig.ClassPath != "" { + args = append(args, "-cp", driverConfig.ClassPath) + } + + // Add the jar + if driverConfig.JarPath != "" { + args = append(args, "-jar", driverConfig.JarPath) + } + + // Add the class + if driverConfig.Class != "" { + args = append(args, driverConfig.Class) + } + + // Add any args if len(driverConfig.Args) != 0 { args = append(args, driverConfig.Args...) } diff --git a/client/driver/java_linux.go b/client/driver/java_linux.go new file mode 100644 index 000000000000..99a8419689bb --- /dev/null +++ b/client/driver/java_linux.go @@ -0,0 +1,7 @@ +package driver + +import cstructs "github.com/hashicorp/nomad/client/structs" + +func (d *JavaDriver) FSIsolation() cstructs.FSIsolation { + return cstructs.FSIsolationChroot +} diff --git a/client/driver/java_test.go b/client/driver/java_test.go index f0821c710b93..8fa5ec26b179 100644 --- a/client/driver/java_test.go +++ b/client/driver/java_test.go @@ -349,3 +349,70 @@ func TestJavaDriverUser(t *testing.T) { t.Fatalf("Expecting '%v' in '%v'", msg, err) } } + +func TestJavaDriver_Start_Wait_Class(t *testing.T) { + if !javaLocated() { + t.Skip("Java not found; skipping") + } + + ctestutils.JavaCompatible(t) + task := &structs.Task{ + Name: "demo-app", + Driver: "java", + Config: map[string]interface{}{ + "class_path": "${NOMAD_TASK_DIR}", + "class": "Hello", + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + Resources: basicResources, + } + + ctx := testDriverContexts(t, task) + //defer ctx.AllocDir.Destroy() + d := NewJavaDriver(ctx.DriverCtx) + + // Copy the test jar into the task's directory + dst := ctx.ExecCtx.TaskDir.LocalDir + copyFile("./test-resources/java/Hello.class", filepath.Join(dst, "Hello.class"), t) + + if err := d.Prestart(ctx.ExecCtx, task); err != nil { + t.Fatalf("prestart err: %v", err) + } + handle, err := d.Start(ctx.ExecCtx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + + // Task should terminate quickly + select { + case res := <-handle.WaitCh(): + if !res.Successful() { + t.Fatalf("err: %v", res) + } + case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): + // expect the timeout b/c it's a long lived process + break + } + + // Get the stdout of the process and assrt that it's not empty + stdout := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "demo-app.stdout.0") + fInfo, err := os.Stat(stdout) + if err != nil { + t.Fatalf("failed to get stdout of process: %v", err) + } + if fInfo.Size() == 0 { + t.Fatalf("stdout of process is empty") + } + + // need to kill long lived process + err = handle.Kill() + if err != nil { + t.Fatalf("Error: %s", err) + } +} diff --git a/client/driver/java_universal.go b/client/driver/java_universal.go new file mode 100644 index 000000000000..a75869b9d1b6 --- /dev/null +++ b/client/driver/java_universal.go @@ -0,0 +1,9 @@ +// +build !linux + +package driver + +import cstructs "github.com/hashicorp/nomad/client/structs" + +func (d *JavaDriver) FSIsolation() cstructs.FSIsolation { + return cstructs.FSIsolationNone +} diff --git a/client/driver/test-resources/java/Hello.class b/client/driver/test-resources/java/Hello.class new file mode 100644 index 0000000000000000000000000000000000000000..08a9b2e100cda1d4715aabfa8c1e511d9fe0272b GIT binary patch literal 638 zcmZuvO;6iE5Pj=>Y+_7kAfat2wDbcUTF9k0s$MEkTZIoPsF(KQI4kPZu`RQ%+F!)E z94P96AHXl+%ntzGI;sGPJQk?X$~&rvTofW5dD2Djwl+1uYwMcv514r*Lj# z9t#prjelm$qC`vLxj^|{5C!_ZK*4Q)5h#9$2TEY78$@b58}(JX=k-HoiX$(G1ZLf@ z-J$o>>x5pk-|6Wzi1yb_<77{J{*Ns$$?+T|VE5uI_0?uz`t6S@4C6J^>tGT!i5CuD z;*~%tuShI8ScWUncCdoi0t+{DK1EukX_ja;*!bnELk3IAftX-am*tyIfvVg7QY|0y8H%kq8ewpK$U~WDzS(OeoJ#K@M%$_onkakFX&4vefz#3984i53kM%t&qD;Yvn{OQUOKAW#$c? XBw>ZWR24I