github.com/jmitchell/nomad@v0.1.3-0.20151007230021-7ab84c2862d8/client/driver/java.go (about) 1 package driver 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "os/exec" 10 "path" 11 "path/filepath" 12 "runtime" 13 "strings" 14 "syscall" 15 "time" 16 17 "github.com/hashicorp/nomad/client/allocdir" 18 "github.com/hashicorp/nomad/client/config" 19 "github.com/hashicorp/nomad/client/executor" 20 "github.com/hashicorp/nomad/nomad/structs" 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 } 28 29 // javaHandle is returned from Start/Open as a handle to the PID 30 type javaHandle struct { 31 cmd executor.Executor 32 waitCh chan error 33 doneCh chan struct{} 34 } 35 36 // NewJavaDriver is used to create a new exec driver 37 func NewJavaDriver(ctx *DriverContext) Driver { 38 return &JavaDriver{*ctx} 39 } 40 41 func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 42 // Only enable if we are root when running on non-windows systems. 43 if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { 44 d.logger.Printf("[DEBUG] driver.java: must run as root user, disabling") 45 return false, nil 46 } 47 48 // Find java version 49 var out bytes.Buffer 50 var erOut bytes.Buffer 51 cmd := exec.Command("java", "-version") 52 cmd.Stdout = &out 53 cmd.Stderr = &erOut 54 err := cmd.Run() 55 if err != nil { 56 // assume Java wasn't found 57 return false, nil 58 } 59 60 // 'java -version' returns output on Stderr typically. 61 // Check stdout, but it's probably empty 62 var infoString string 63 if out.String() != "" { 64 infoString = out.String() 65 } 66 67 if erOut.String() != "" { 68 infoString = erOut.String() 69 } 70 71 if infoString == "" { 72 d.logger.Println("[WARN] Error parsing Java version information, aborting") 73 return false, nil 74 } 75 76 // Assume 'java -version' returns 3 lines: 77 // java version "1.6.0_36" 78 // OpenJDK Runtime Environment (IcedTea6 1.13.8) (6b36-1.13.8-0ubuntu1~12.04) 79 // OpenJDK 64-Bit Server VM (build 23.25-b01, mixed mode) 80 // Each line is terminated by \n 81 info := strings.Split(infoString, "\n") 82 versionString := info[0] 83 versionString = strings.TrimPrefix(versionString, "java version ") 84 versionString = strings.Trim(versionString, "\"") 85 node.Attributes["driver.java"] = "1" 86 node.Attributes["driver.java.version"] = versionString 87 node.Attributes["driver.java.runtime"] = info[1] 88 node.Attributes["driver.java.vm"] = info[2] 89 90 return true, nil 91 } 92 93 func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 94 // Get the jar source 95 source, ok := task.Config["jar_source"] 96 if !ok || source == "" { 97 return nil, fmt.Errorf("missing jar source for Java Jar driver") 98 } 99 100 // Attempt to download the thing 101 // Should be extracted to some kind of Http Fetcher 102 // Right now, assume publicly accessible HTTP url 103 resp, err := http.Get(source) 104 if err != nil { 105 return nil, fmt.Errorf("Error downloading source for Java driver: %s", err) 106 } 107 108 // Get the tasks local directory. 109 taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] 110 if !ok { 111 return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) 112 } 113 taskLocal := filepath.Join(taskDir, allocdir.TaskLocal) 114 115 // Create a location to download the binary. 116 fName := path.Base(source) 117 fPath := filepath.Join(taskLocal, fName) 118 f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY, 0666) 119 if err != nil { 120 return nil, fmt.Errorf("Error opening file to download to: %s", err) 121 } 122 123 defer f.Close() 124 defer resp.Body.Close() 125 126 // Copy remote file to local directory for execution 127 // TODO: a retry of sort if io.Copy fails, for large binaries 128 _, ioErr := io.Copy(f, resp.Body) 129 if ioErr != nil { 130 return nil, fmt.Errorf("Error copying jar from source: %s", ioErr) 131 } 132 133 // Get the environment variables. 134 envVars := TaskEnvironmentVariables(ctx, task) 135 136 // Build the argument list. 137 args := []string{"-jar", filepath.Join(allocdir.TaskLocal, fName)} 138 if argRaw, ok := task.Config["args"]; ok { 139 args = append(args, argRaw) 140 } 141 142 // Setup the command 143 // Assumes Java is in the $PATH, but could probably be detected 144 cmd := executor.Command("java", args...) 145 146 // Populate environment variables 147 cmd.Command().Env = envVars.List() 148 149 if err := cmd.Limit(task.Resources); err != nil { 150 return nil, fmt.Errorf("failed to constrain resources: %s", err) 151 } 152 153 if err := cmd.ConfigureTaskDir(d.taskName, ctx.AllocDir); err != nil { 154 return nil, fmt.Errorf("failed to configure task directory: %v", err) 155 } 156 157 if err := cmd.Start(); err != nil { 158 return nil, fmt.Errorf("failed to start source: %v", err) 159 } 160 161 // Return a driver handle 162 h := &javaHandle{ 163 cmd: cmd, 164 doneCh: make(chan struct{}), 165 waitCh: make(chan error, 1), 166 } 167 168 go h.run() 169 return h, nil 170 } 171 172 func (d *JavaDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 173 // Find the process 174 cmd, err := executor.OpenId(handleID) 175 if err != nil { 176 return nil, fmt.Errorf("failed to open ID %v: %v", handleID, err) 177 } 178 179 // Return a driver handle 180 h := &javaHandle{ 181 cmd: cmd, 182 doneCh: make(chan struct{}), 183 waitCh: make(chan error, 1), 184 } 185 186 go h.run() 187 return h, nil 188 } 189 190 func (h *javaHandle) ID() string { 191 id, _ := h.cmd.ID() 192 return id 193 } 194 195 func (h *javaHandle) WaitCh() chan error { 196 return h.waitCh 197 } 198 199 func (h *javaHandle) Update(task *structs.Task) error { 200 // Update is not possible 201 return nil 202 } 203 204 func (h *javaHandle) Kill() error { 205 h.cmd.Shutdown() 206 select { 207 case <-h.doneCh: 208 return nil 209 case <-time.After(5 * time.Second): 210 return h.cmd.ForceStop() 211 } 212 } 213 214 func (h *javaHandle) run() { 215 err := h.cmd.Wait() 216 close(h.doneCh) 217 if err != nil { 218 h.waitCh <- err 219 } 220 close(h.waitCh) 221 }