github.com/ranjib/nomad@v0.1.1-0.20160225204057-97751b02f70b/client/driver/raw_exec.go (about) 1 package driver 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "os/exec" 8 "path/filepath" 9 "time" 10 11 "github.com/hashicorp/go-plugin" 12 "github.com/hashicorp/nomad/client/allocdir" 13 "github.com/hashicorp/nomad/client/config" 14 "github.com/hashicorp/nomad/client/driver/executor" 15 cstructs "github.com/hashicorp/nomad/client/driver/structs" 16 "github.com/hashicorp/nomad/client/fingerprint" 17 "github.com/hashicorp/nomad/client/getter" 18 "github.com/hashicorp/nomad/helper/discover" 19 "github.com/hashicorp/nomad/nomad/structs" 20 "github.com/mitchellh/mapstructure" 21 ) 22 23 const ( 24 // The option that enables this driver in the Config.Options map. 25 rawExecConfigOption = "driver.raw_exec.enable" 26 ) 27 28 // The RawExecDriver is a privileged version of the exec driver. It provides no 29 // resource isolation and just fork/execs. The Exec driver should be preferred 30 // and this should only be used when explicitly needed. 31 type RawExecDriver struct { 32 DriverContext 33 fingerprint.StaticFingerprinter 34 } 35 36 // rawExecHandle is returned from Start/Open as a handle to the PID 37 type rawExecHandle struct { 38 version string 39 pluginClient *plugin.Client 40 userPid int 41 executor executor.Executor 42 killTimeout time.Duration 43 allocDir *allocdir.AllocDir 44 logger *log.Logger 45 waitCh chan *cstructs.WaitResult 46 doneCh chan struct{} 47 } 48 49 // NewRawExecDriver is used to create a new raw exec driver 50 func NewRawExecDriver(ctx *DriverContext) Driver { 51 return &RawExecDriver{DriverContext: *ctx} 52 } 53 54 func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 55 // Check that the user has explicitly enabled this executor. 56 enabled := cfg.ReadBoolDefault(rawExecConfigOption, false) 57 58 if enabled { 59 d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed") 60 node.Attributes["driver.raw_exec"] = "1" 61 return true, nil 62 } 63 64 return false, nil 65 } 66 67 func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 68 var driverConfig ExecDriverConfig 69 if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { 70 return nil, err 71 } 72 // Get the tasks local directory. 73 taskName := d.DriverContext.taskName 74 taskDir, ok := ctx.AllocDir.TaskDirs[taskName] 75 if !ok { 76 return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) 77 } 78 79 // Get the command to be ran 80 command := driverConfig.Command 81 if err := validateCommand(command, "args"); err != nil { 82 return nil, err 83 } 84 85 // Check if an artificat is specified and attempt to download it 86 source, ok := task.Config["artifact_source"] 87 if ok && source != "" { 88 // Proceed to download an artifact to be executed. 89 _, err := getter.GetArtifact( 90 taskDir, 91 driverConfig.ArtifactSource, 92 driverConfig.Checksum, 93 d.logger, 94 ) 95 if err != nil { 96 return nil, err 97 } 98 } 99 100 bin, err := discover.NomadExecutable() 101 if err != nil { 102 return nil, fmt.Errorf("unable to find the nomad binary: %v", err) 103 } 104 pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name)) 105 pluginConfig := &plugin.ClientConfig{ 106 Cmd: exec.Command(bin, "executor", pluginLogFile), 107 } 108 109 exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) 110 if err != nil { 111 return nil, err 112 } 113 executorCtx := &executor.ExecutorContext{ 114 TaskEnv: d.taskEnv, 115 AllocDir: ctx.AllocDir, 116 TaskName: task.Name, 117 TaskResources: task.Resources, 118 LogConfig: task.LogConfig, 119 } 120 ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx) 121 if err != nil { 122 pluginClient.Kill() 123 return nil, fmt.Errorf("error starting process via the plugin: %v", err) 124 } 125 d.logger.Printf("[DEBUG] driver.raw_exec: started process with pid: %v", ps.Pid) 126 127 // Return a driver handle 128 h := &rawExecHandle{ 129 pluginClient: pluginClient, 130 executor: exec, 131 userPid: ps.Pid, 132 killTimeout: d.DriverContext.KillTimeout(task), 133 allocDir: ctx.AllocDir, 134 version: d.config.Version, 135 logger: d.logger, 136 doneCh: make(chan struct{}), 137 waitCh: make(chan *cstructs.WaitResult, 1), 138 } 139 go h.run() 140 return h, nil 141 } 142 143 type rawExecId struct { 144 Version string 145 KillTimeout time.Duration 146 UserPid int 147 PluginConfig *PluginReattachConfig 148 AllocDir *allocdir.AllocDir 149 } 150 151 func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 152 id := &rawExecId{} 153 if err := json.Unmarshal([]byte(handleID), id); err != nil { 154 return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) 155 } 156 157 pluginConfig := &plugin.ClientConfig{ 158 Reattach: id.PluginConfig.PluginConfig(), 159 } 160 executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) 161 if err != nil { 162 d.logger.Println("[ERR] driver.raw_exec: error connecting to plugin so destroying plugin pid and user pid") 163 if e := destroyPlugin(id.PluginConfig.Pid, id.UserPid); e != nil { 164 d.logger.Printf("[ERR] driver.raw_exec: error destroying plugin and userpid: %v", e) 165 } 166 return nil, fmt.Errorf("error connecting to plugin: %v", err) 167 } 168 169 // Return a driver handle 170 h := &rawExecHandle{ 171 pluginClient: pluginClient, 172 executor: executor, 173 userPid: id.UserPid, 174 logger: d.logger, 175 killTimeout: id.KillTimeout, 176 allocDir: id.AllocDir, 177 version: id.Version, 178 doneCh: make(chan struct{}), 179 waitCh: make(chan *cstructs.WaitResult, 1), 180 } 181 go h.run() 182 return h, nil 183 } 184 185 func (h *rawExecHandle) ID() string { 186 id := rawExecId{ 187 Version: h.version, 188 KillTimeout: h.killTimeout, 189 PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()), 190 UserPid: h.userPid, 191 AllocDir: h.allocDir, 192 } 193 194 data, err := json.Marshal(id) 195 if err != nil { 196 h.logger.Printf("[ERR] driver.raw_exec: failed to marshal ID to JSON: %s", err) 197 } 198 return string(data) 199 } 200 201 func (h *rawExecHandle) WaitCh() chan *cstructs.WaitResult { 202 return h.waitCh 203 } 204 205 func (h *rawExecHandle) Update(task *structs.Task) error { 206 // Store the updated kill timeout. 207 h.killTimeout = task.KillTimeout 208 h.executor.UpdateLogConfig(task.LogConfig) 209 210 // Update is not possible 211 return nil 212 } 213 214 func (h *rawExecHandle) Kill() error { 215 if err := h.executor.ShutDown(); err != nil { 216 if h.pluginClient.Exited() { 217 return nil 218 } 219 return fmt.Errorf("executor Shutdown failed: %v", err) 220 } 221 222 select { 223 case <-h.doneCh: 224 return nil 225 case <-time.After(h.killTimeout): 226 if h.pluginClient.Exited() { 227 return nil 228 } 229 if err := h.executor.Exit(); err != nil { 230 return fmt.Errorf("executor Exit failed: %v", err) 231 } 232 233 return nil 234 } 235 } 236 237 func (h *rawExecHandle) run() { 238 ps, err := h.executor.Wait() 239 close(h.doneCh) 240 if ps.ExitCode == 0 && err != nil { 241 if e := killProcess(h.userPid); e != nil { 242 h.logger.Printf("[ERR] driver.raw_exec: error killing user process: %v", e) 243 } 244 if e := h.allocDir.UnmountAll(); e != nil { 245 h.logger.Printf("[ERR] driver.raw_exec: unmounting dev,proc and alloc dirs failed: %v", e) 246 } 247 } 248 h.waitCh <- &cstructs.WaitResult{ExitCode: ps.ExitCode, Signal: 0, Err: err} 249 close(h.waitCh) 250 h.pluginClient.Kill() 251 }