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