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 }