mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
Merge pull request #2199 from hashicorp/f-jars
Support setting class_path and class name.
This commit is contained in:
@@ -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...)
|
||||
}
|
||||
|
||||
7
client/driver/java_linux.go
Normal file
7
client/driver/java_linux.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package driver
|
||||
|
||||
import cstructs "github.com/hashicorp/nomad/client/structs"
|
||||
|
||||
func (d *JavaDriver) FSIsolation() cstructs.FSIsolation {
|
||||
return cstructs.FSIsolationChroot
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
9
client/driver/java_universal.go
Normal file
9
client/driver/java_universal.go
Normal file
@@ -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
|
||||
}
|
||||
BIN
client/driver/test-resources/java/Hello.class
Normal file
BIN
client/driver/test-resources/java/Hello.class
Normal file
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
public class Hello {
|
||||
public static void main(String[] args) {
|
||||
while (true) {
|
||||
System.out.println("Hi");
|
||||
System.out.println("Hello");
|
||||
try {
|
||||
Thread.sleep(1000); //1000 milliseconds is one second.
|
||||
} catch(InterruptedException ex) {
|
||||
@@ -29,7 +29,14 @@ task "webservice" {
|
||||
|
||||
The `java` driver supports the following configuration in the job spec:
|
||||
|
||||
* `jar_path` - The path to the downloaded Jar. In most cases this will just be
|
||||
* `class` - (Optional) The name of the class to run. If `jar_path` is specified
|
||||
and the manifest specifies a main class, this is optional. If shipping classes
|
||||
rather than a Jar, please specify the class to run and the `class_path`.
|
||||
|
||||
* `class_path` - (Optional) The `class_path` specifies the clath path used by
|
||||
Java to lookup classes and Jars.
|
||||
|
||||
* `jar_path` - (Optional) The path to the downloaded Jar. In most cases this will just be
|
||||
the name of the Jar. However, if the supplied artifact is an archive that
|
||||
contains the Jar in a subfolder, the path will need to be the relative path
|
||||
(`subdir/from_archive/my.jar`).
|
||||
@@ -67,6 +74,30 @@ task "web" {
|
||||
}
|
||||
```
|
||||
|
||||
A simple config block to run a Java class:
|
||||
|
||||
```hcl
|
||||
task "web" {
|
||||
driver = "java"
|
||||
|
||||
config {
|
||||
class = "Hello"
|
||||
class_path = "${NOMAD_TASK_DIR}"
|
||||
jvm_options = ["-Xmx2048m", "-Xms256m"]
|
||||
}
|
||||
|
||||
# Specifying an artifact is required with the "java" driver. This is the
|
||||
# mechanism to ship the Jar to be run.
|
||||
artifact {
|
||||
source = "https://internal.file.server/Hello.class"
|
||||
|
||||
options {
|
||||
checksum = "md5:123445555555555"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Client Requirements
|
||||
|
||||
The `java` driver requires Java to be installed and in your system's `$PATH`. On
|
||||
|
||||
Reference in New Issue
Block a user