github.com/ncodes/nomad@v0.5.7-0.20170403112158-97adf4a74fb3/client/driver/raw_exec.go (about) 1 package driver 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "log" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/hashicorp/go-plugin" 12 "github.com/mitchellh/mapstructure" 13 "github.com/ncodes/nomad/client/config" 14 "github.com/ncodes/nomad/client/driver/executor" 15 dstructs "github.com/ncodes/nomad/client/driver/structs" 16 "github.com/ncodes/nomad/client/fingerprint" 17 cstructs "github.com/ncodes/nomad/client/structs" 18 "github.com/ncodes/nomad/helper/fields" 19 "github.com/ncodes/nomad/nomad/structs" 20 ) 21 22 const ( 23 // The option that enables this driver in the Config.Options map. 24 rawExecConfigOption = "driver.raw_exec.enable" 25 26 // The key populated in Node Attributes to indicate presence of the Raw Exec 27 // driver 28 rawExecDriverAttr = "driver.raw_exec" 29 ) 30 31 // The RawExecDriver is a privileged version of the exec driver. It provides no 32 // resource isolation and just fork/execs. The Exec driver should be preferred 33 // and this should only be used when explicitly needed. 34 type RawExecDriver struct { 35 DriverContext 36 fingerprint.StaticFingerprinter 37 } 38 39 // rawExecHandle is returned from Start/Open as a handle to the PID 40 type rawExecHandle struct { 41 version string 42 pluginClient *plugin.Client 43 userPid int 44 executor executor.Executor 45 killTimeout time.Duration 46 maxKillTimeout time.Duration 47 logger *log.Logger 48 waitCh chan *dstructs.WaitResult 49 doneCh chan struct{} 50 execCtx *executor.ExecutorContext 51 } 52 53 // NewRawExecDriver is used to create a new raw exec driver 54 func NewRawExecDriver(ctx *DriverContext) Driver { 55 return &RawExecDriver{DriverContext: *ctx} 56 } 57 58 // Validate is used to validate the driver configuration 59 func (d *RawExecDriver) Validate(config map[string]interface{}) error { 60 fd := &fields.FieldData{ 61 Raw: config, 62 Schema: map[string]*fields.FieldSchema{ 63 "command": &fields.FieldSchema{ 64 Type: fields.TypeString, 65 Required: true, 66 }, 67 "args": &fields.FieldSchema{ 68 Type: fields.TypeArray, 69 }, 70 }, 71 } 72 73 if err := fd.Validate(); err != nil { 74 return err 75 } 76 77 return nil 78 } 79 80 func (d *RawExecDriver) Abilities() DriverAbilities { 81 return DriverAbilities{ 82 SendSignals: true, 83 } 84 } 85 86 func (d *RawExecDriver) FSIsolation() cstructs.FSIsolation { 87 return cstructs.FSIsolationNone 88 } 89 90 func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 91 // Check that the user has explicitly enabled this executor. 92 enabled := cfg.ReadBoolDefault(rawExecConfigOption, false) 93 94 if enabled || cfg.DevMode { 95 d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed") 96 node.Attributes[rawExecDriverAttr] = "1" 97 return true, nil 98 } 99 100 delete(node.Attributes, rawExecDriverAttr) 101 return false, nil 102 } 103 104 func (d *RawExecDriver) Prestart(*ExecContext, *structs.Task) (*CreatedResources, error) { 105 return nil, nil 106 } 107 108 func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 109 110 var driverConfig ExecDriverConfig 111 if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { 112 return nil, err 113 } 114 115 // Get the command to be ran 116 command := driverConfig.Command 117 if err := validateCommand(command, "args"); err != nil { 118 return nil, err 119 } 120 121 pluginLogFile := filepath.Join(ctx.TaskDir.Dir, "executor.out") 122 executorConfig := &dstructs.ExecutorConfig{ 123 LogFile: pluginLogFile, 124 LogLevel: d.config.LogLevel, 125 } 126 127 exec, pluginClient, err := createExecutor(d.config.LogOutput, d.config, executorConfig) 128 if err != nil { 129 return nil, err 130 } 131 132 executorCtx := &executor.ExecutorContext{ 133 TaskEnv: d.taskEnv, 134 Driver: "raw_exec", 135 AllocID: d.DriverContext.allocID, 136 Task: task, 137 TaskDir: ctx.TaskDir.Dir, 138 LogDir: ctx.TaskDir.LogDir, 139 } 140 if err := exec.SetContext(executorCtx); err != nil { 141 pluginClient.Kill() 142 return nil, fmt.Errorf("failed to set executor context: %v", err) 143 } 144 145 execCmd := &executor.ExecCommand{ 146 Cmd: command, 147 Args: driverConfig.Args, 148 User: task.User, 149 } 150 ps, err := exec.LaunchCmd(execCmd) 151 if err != nil { 152 pluginClient.Kill() 153 return nil, err 154 } 155 d.logger.Printf("[DEBUG] driver.raw_exec: started process with pid: %v", ps.Pid) 156 157 // Return a driver handle 158 maxKill := d.DriverContext.config.MaxKillTimeout 159 h := &rawExecHandle{ 160 pluginClient: pluginClient, 161 executor: exec, 162 userPid: ps.Pid, 163 killTimeout: GetKillTimeout(task.KillTimeout, maxKill), 164 maxKillTimeout: maxKill, 165 version: d.config.Version, 166 logger: d.logger, 167 doneCh: make(chan struct{}), 168 waitCh: make(chan *dstructs.WaitResult, 1), 169 execCtx: executorCtx, 170 } 171 if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil { 172 h.logger.Printf("[ERR] driver.raw_exec: error registering services with consul for task: %q: %v", task.Name, err) 173 } 174 go h.run() 175 return h, nil 176 } 177 178 func (d *RawExecDriver) Cleanup(execCtx *ExecContext, cr *CreatedResources) error { 179 return nil 180 } 181 182 type rawExecId struct { 183 Version string 184 KillTimeout time.Duration 185 MaxKillTimeout time.Duration 186 UserPid int 187 PluginConfig *PluginReattachConfig 188 } 189 190 func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 191 id := &rawExecId{} 192 if err := json.Unmarshal([]byte(handleID), id); err != nil { 193 return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) 194 } 195 196 pluginConfig := &plugin.ClientConfig{ 197 Reattach: id.PluginConfig.PluginConfig(), 198 } 199 exec, pluginClient, err := createExecutorWithConfig(pluginConfig, d.config.LogOutput) 200 if err != nil { 201 d.logger.Println("[ERR] driver.raw_exec: error connecting to plugin so destroying plugin pid and user pid") 202 if e := destroyPlugin(id.PluginConfig.Pid, id.UserPid); e != nil { 203 d.logger.Printf("[ERR] driver.raw_exec: error destroying plugin and userpid: %v", e) 204 } 205 return nil, fmt.Errorf("error connecting to plugin: %v", err) 206 } 207 208 ver, _ := exec.Version() 209 d.logger.Printf("[DEBUG] driver.raw_exec: version of executor: %v", ver.Version) 210 211 // Return a driver handle 212 h := &rawExecHandle{ 213 pluginClient: pluginClient, 214 executor: exec, 215 userPid: id.UserPid, 216 logger: d.logger, 217 killTimeout: id.KillTimeout, 218 maxKillTimeout: id.MaxKillTimeout, 219 version: id.Version, 220 doneCh: make(chan struct{}), 221 waitCh: make(chan *dstructs.WaitResult, 1), 222 } 223 if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil { 224 h.logger.Printf("[ERR] driver.raw_exec: error registering services with consul: %v", err) 225 } 226 go h.run() 227 return h, nil 228 } 229 230 func (h *rawExecHandle) ID() string { 231 id := rawExecId{ 232 Version: h.version, 233 KillTimeout: h.killTimeout, 234 MaxKillTimeout: h.maxKillTimeout, 235 PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()), 236 UserPid: h.userPid, 237 } 238 239 data, err := json.Marshal(id) 240 if err != nil { 241 h.logger.Printf("[ERR] driver.raw_exec: failed to marshal ID to JSON: %s", err) 242 } 243 return string(data) 244 } 245 246 func (h *rawExecHandle) WaitCh() chan *dstructs.WaitResult { 247 return h.waitCh 248 } 249 250 func (h *rawExecHandle) Update(task *structs.Task) error { 251 // Store the updated kill timeout. 252 h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout) 253 h.executor.UpdateTask(task) 254 255 // Update is not possible 256 return nil 257 } 258 259 func (h *rawExecHandle) Signal(s os.Signal) error { 260 return h.executor.Signal(s) 261 } 262 263 func (h *rawExecHandle) Kill() error { 264 265 if err := h.executor.ShutDown(); err != nil { 266 if h.pluginClient.Exited() { 267 return nil 268 } 269 return fmt.Errorf("executor Shutdown failed: %v", err) 270 } 271 272 select { 273 case <-h.doneCh: 274 return nil 275 case <-time.After(h.killTimeout): 276 if h.pluginClient.Exited() { 277 return nil 278 } 279 if err := h.executor.Exit(); err != nil { 280 return fmt.Errorf("executor Exit failed: %v", err) 281 } 282 283 return nil 284 } 285 } 286 287 func (h *rawExecHandle) Stats() (*cstructs.TaskResourceUsage, error) { 288 return h.executor.Stats() 289 } 290 291 func (h *rawExecHandle) run() { 292 ps, werr := h.executor.Wait() 293 close(h.doneCh) 294 if ps.ExitCode == 0 && werr != nil { 295 if e := killProcess(h.userPid); e != nil { 296 h.logger.Printf("[ERR] driver.raw_exec: error killing user process: %v", e) 297 } 298 } 299 // Remove services 300 if err := h.executor.DeregisterServices(); err != nil { 301 h.logger.Printf("[ERR] driver.raw_exec: failed to deregister services: %v", err) 302 } 303 304 // Exit the executor 305 if err := h.executor.Exit(); err != nil { 306 h.logger.Printf("[ERR] driver.raw_exec: error killing executor: %v", err) 307 } 308 h.pluginClient.Kill() 309 310 // Send the results 311 h.waitCh <- &dstructs.WaitResult{ExitCode: ps.ExitCode, Signal: ps.Signal, Err: werr} 312 close(h.waitCh) 313 }