github.com/huiliang/nomad@v0.2.1-0.20151124023127-7a8b664699ff/client/driver/java.go (about)

     1  package driver
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/hashicorp/nomad/client/allocdir"
    14  	"github.com/hashicorp/nomad/client/config"
    15  	"github.com/hashicorp/nomad/client/driver/executor"
    16  	cstructs "github.com/hashicorp/nomad/client/driver/structs"
    17  	"github.com/hashicorp/nomad/client/fingerprint"
    18  	"github.com/hashicorp/nomad/client/getter"
    19  	"github.com/hashicorp/nomad/nomad/structs"
    20  	"github.com/mitchellh/mapstructure"
    21  )
    22  
    23  // JavaDriver is a simple driver to execute applications packaged in Jars.
    24  // It literally just fork/execs tasks with the java command.
    25  type JavaDriver struct {
    26  	DriverContext
    27  	fingerprint.StaticFingerprinter
    28  }
    29  
    30  type JavaDriverConfig struct {
    31  	JvmOpts        []string `mapstructure:"jvm_options"`
    32  	ArtifactSource string   `mapstructure:"artifact_source"`
    33  	Checksum       string   `mapstructure:"checksum"`
    34  	Args           []string `mapstructure:"args"`
    35  }
    36  
    37  // javaHandle is returned from Start/Open as a handle to the PID
    38  type javaHandle struct {
    39  	cmd    executor.Executor
    40  	waitCh chan *cstructs.WaitResult
    41  	doneCh chan struct{}
    42  }
    43  
    44  // NewJavaDriver is used to create a new exec driver
    45  func NewJavaDriver(ctx *DriverContext) Driver {
    46  	return &JavaDriver{DriverContext: *ctx}
    47  }
    48  
    49  func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
    50  	// Only enable if we are root when running on non-windows systems.
    51  	if runtime.GOOS == "linux" && syscall.Geteuid() != 0 {
    52  		d.logger.Printf("[DEBUG] driver.java: must run as root user on linux, disabling")
    53  		return false, nil
    54  	}
    55  
    56  	// Find java version
    57  	var out bytes.Buffer
    58  	var erOut bytes.Buffer
    59  	cmd := exec.Command("java", "-version")
    60  	cmd.Stdout = &out
    61  	cmd.Stderr = &erOut
    62  	err := cmd.Run()
    63  	if err != nil {
    64  		// assume Java wasn't found
    65  		return false, nil
    66  	}
    67  
    68  	// 'java -version' returns output on Stderr typically.
    69  	// Check stdout, but it's probably empty
    70  	var infoString string
    71  	if out.String() != "" {
    72  		infoString = out.String()
    73  	}
    74  
    75  	if erOut.String() != "" {
    76  		infoString = erOut.String()
    77  	}
    78  
    79  	if infoString == "" {
    80  		d.logger.Println("[WARN] driver.java: error parsing Java version information, aborting")
    81  		return false, nil
    82  	}
    83  
    84  	// Assume 'java -version' returns 3 lines:
    85  	//    java version "1.6.0_36"
    86  	//    OpenJDK Runtime Environment (IcedTea6 1.13.8) (6b36-1.13.8-0ubuntu1~12.04)
    87  	//    OpenJDK 64-Bit Server VM (build 23.25-b01, mixed mode)
    88  	// Each line is terminated by \n
    89  	info := strings.Split(infoString, "\n")
    90  	versionString := info[0]
    91  	versionString = strings.TrimPrefix(versionString, "java version ")
    92  	versionString = strings.Trim(versionString, "\"")
    93  	node.Attributes["driver.java"] = "1"
    94  	node.Attributes["driver.java.version"] = versionString
    95  	node.Attributes["driver.java.runtime"] = info[1]
    96  	node.Attributes["driver.java.vm"] = info[2]
    97  
    98  	return true, nil
    99  }
   100  
   101  func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
   102  	var driverConfig JavaDriverConfig
   103  	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
   104  		return nil, err
   105  	}
   106  	taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName]
   107  	if !ok {
   108  		return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
   109  	}
   110  
   111  	// Proceed to download an artifact to be executed.
   112  	path, err := getter.GetArtifact(
   113  		filepath.Join(taskDir, allocdir.TaskLocal),
   114  		driverConfig.ArtifactSource,
   115  		driverConfig.Checksum,
   116  		d.logger,
   117  	)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	jarName := filepath.Base(path)
   123  
   124  	// Get the environment variables.
   125  	envVars := TaskEnvironmentVariables(ctx, task)
   126  
   127  	args := []string{}
   128  	// Look for jvm options
   129  	if len(driverConfig.JvmOpts) != 0 {
   130  		d.logger.Printf("[DEBUG] driver.java: found JVM options: %s", driverConfig.JvmOpts)
   131  		args = append(args, driverConfig.JvmOpts...)
   132  	}
   133  
   134  	// Build the argument list.
   135  	args = append(args, "-jar", filepath.Join(allocdir.TaskLocal, jarName))
   136  	if len(driverConfig.Args) != 0 {
   137  		args = append(args, driverConfig.Args...)
   138  	}
   139  
   140  	// Setup the command
   141  	// Assumes Java is in the $PATH, but could probably be detected
   142  	cmd := executor.Command("java", args...)
   143  
   144  	// Populate environment variables
   145  	cmd.Command().Env = envVars.List()
   146  
   147  	if err := cmd.Limit(task.Resources); err != nil {
   148  		return nil, fmt.Errorf("failed to constrain resources: %s", err)
   149  	}
   150  
   151  	if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil {
   152  		return nil, fmt.Errorf("failed to configure task directory: %v", err)
   153  	}
   154  
   155  	if err := cmd.Start(); err != nil {
   156  		return nil, fmt.Errorf("failed to start source: %v", err)
   157  	}
   158  
   159  	// Return a driver handle
   160  	h := &javaHandle{
   161  		cmd:    cmd,
   162  		doneCh: make(chan struct{}),
   163  		waitCh: make(chan *cstructs.WaitResult, 1),
   164  	}
   165  
   166  	go h.run()
   167  	return h, nil
   168  }
   169  
   170  func (d *JavaDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
   171  	// Find the process
   172  	cmd, err := executor.OpenId(handleID)
   173  	if err != nil {
   174  		return nil, fmt.Errorf("failed to open ID %v: %v", handleID, err)
   175  	}
   176  
   177  	// Return a driver handle
   178  	h := &javaHandle{
   179  		cmd:    cmd,
   180  		doneCh: make(chan struct{}),
   181  		waitCh: make(chan *cstructs.WaitResult, 1),
   182  	}
   183  
   184  	go h.run()
   185  	return h, nil
   186  }
   187  
   188  func (h *javaHandle) ID() string {
   189  	id, _ := h.cmd.ID()
   190  	return id
   191  }
   192  
   193  func (h *javaHandle) WaitCh() chan *cstructs.WaitResult {
   194  	return h.waitCh
   195  }
   196  
   197  func (h *javaHandle) Update(task *structs.Task) error {
   198  	// Update is not possible
   199  	return nil
   200  }
   201  
   202  func (h *javaHandle) Kill() error {
   203  	h.cmd.Shutdown()
   204  	select {
   205  	case <-h.doneCh:
   206  		return nil
   207  	case <-time.After(5 * time.Second):
   208  		return h.cmd.ForceStop()
   209  	}
   210  }
   211  
   212  func (h *javaHandle) run() {
   213  	res := h.cmd.Wait()
   214  	close(h.doneCh)
   215  	h.waitCh <- res
   216  	close(h.waitCh)
   217  }