github.com/mattyr/nomad@v0.3.3-0.20160919021406-3485a065154a/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 "strings" 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/executor" 16 dstructs "github.com/hashicorp/nomad/client/driver/structs" 17 "github.com/hashicorp/nomad/client/fingerprint" 18 cstructs "github.com/hashicorp/nomad/client/structs" 19 "github.com/hashicorp/nomad/helper/discover" 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 allocDir *allocdir.AllocDir 51 logger *log.Logger 52 waitCh chan *dstructs.WaitResult 53 doneCh chan struct{} 54 } 55 56 // NewRawExecDriver is used to create a new raw exec driver 57 func NewRawExecDriver(ctx *DriverContext) Driver { 58 return &RawExecDriver{DriverContext: *ctx} 59 } 60 61 // Validate is used to validate the driver configuration 62 func (d *RawExecDriver) Validate(config map[string]interface{}) error { 63 fd := &fields.FieldData{ 64 Raw: config, 65 Schema: map[string]*fields.FieldSchema{ 66 "command": &fields.FieldSchema{ 67 Type: fields.TypeString, 68 Required: true, 69 }, 70 "args": &fields.FieldSchema{ 71 Type: fields.TypeArray, 72 }, 73 }, 74 } 75 76 if err := fd.Validate(); err != nil { 77 return err 78 } 79 80 return nil 81 } 82 83 func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 84 // Get the current status so that we can log any debug messages only if the 85 // state changes 86 _, currentlyEnabled := node.Attributes[rawExecDriverAttr] 87 88 // Check that the user has explicitly enabled this executor. 89 enabled := cfg.ReadBoolDefault(rawExecConfigOption, false) 90 91 if enabled { 92 if currentlyEnabled { 93 d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed") 94 } 95 node.Attributes[rawExecDriverAttr] = "1" 96 return true, nil 97 } 98 99 delete(node.Attributes, rawExecDriverAttr) 100 return false, nil 101 } 102 103 func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 104 var driverConfig ExecDriverConfig 105 if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { 106 return nil, err 107 } 108 // Get the tasks local directory. 109 taskName := d.DriverContext.taskName 110 taskDir, ok := ctx.AllocDir.TaskDirs[taskName] 111 if !ok { 112 return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) 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 // Set the host environment variables. 122 filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") 123 d.taskEnv.AppendHostEnvvars(filter) 124 125 bin, err := discover.NomadExecutable() 126 if err != nil { 127 return nil, fmt.Errorf("unable to find the nomad binary: %v", err) 128 } 129 pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name)) 130 pluginConfig := &plugin.ClientConfig{ 131 Cmd: exec.Command(bin, "executor", pluginLogFile), 132 } 133 134 exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) 135 if err != nil { 136 return nil, err 137 } 138 executorCtx := &executor.ExecutorContext{ 139 TaskEnv: d.taskEnv, 140 Driver: "raw_exec", 141 AllocDir: ctx.AllocDir, 142 AllocID: ctx.AllocID, 143 Task: task, 144 } 145 146 ps, err := exec.LaunchCmd(&executor.ExecCommand{ 147 Cmd: command, 148 Args: driverConfig.Args, 149 User: task.User, 150 }, executorCtx) 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 allocDir: ctx.AllocDir, 166 version: d.config.Version, 167 logger: d.logger, 168 doneCh: make(chan struct{}), 169 waitCh: make(chan *dstructs.WaitResult, 1), 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 type rawExecId struct { 179 Version string 180 KillTimeout time.Duration 181 MaxKillTimeout time.Duration 182 UserPid int 183 PluginConfig *PluginReattachConfig 184 AllocDir *allocdir.AllocDir 185 } 186 187 func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 188 id := &rawExecId{} 189 if err := json.Unmarshal([]byte(handleID), id); err != nil { 190 return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) 191 } 192 193 pluginConfig := &plugin.ClientConfig{ 194 Reattach: id.PluginConfig.PluginConfig(), 195 } 196 exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) 197 if err != nil { 198 d.logger.Println("[ERR] driver.raw_exec: error connecting to plugin so destroying plugin pid and user pid") 199 if e := destroyPlugin(id.PluginConfig.Pid, id.UserPid); e != nil { 200 d.logger.Printf("[ERR] driver.raw_exec: error destroying plugin and userpid: %v", e) 201 } 202 return nil, fmt.Errorf("error connecting to plugin: %v", err) 203 } 204 205 ver, _ := exec.Version() 206 d.logger.Printf("[DEBUG] driver.raw_exec: version of executor: %v", ver.Version) 207 208 // Return a driver handle 209 h := &rawExecHandle{ 210 pluginClient: pluginClient, 211 executor: exec, 212 userPid: id.UserPid, 213 logger: d.logger, 214 killTimeout: id.KillTimeout, 215 maxKillTimeout: id.MaxKillTimeout, 216 allocDir: id.AllocDir, 217 version: id.Version, 218 doneCh: make(chan struct{}), 219 waitCh: make(chan *dstructs.WaitResult, 1), 220 } 221 if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil { 222 h.logger.Printf("[ERR] driver.raw_exec: error registering services with consul: %v", err) 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 AllocDir: h.allocDir, 236 } 237 238 data, err := json.Marshal(id) 239 if err != nil { 240 h.logger.Printf("[ERR] driver.raw_exec: failed to marshal ID to JSON: %s", err) 241 } 242 return string(data) 243 } 244 245 func (h *rawExecHandle) WaitCh() chan *dstructs.WaitResult { 246 return h.waitCh 247 } 248 249 func (h *rawExecHandle) Update(task *structs.Task) error { 250 // Store the updated kill timeout. 251 h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout) 252 h.executor.UpdateTask(task) 253 254 // Update is not possible 255 return nil 256 } 257 258 func (h *rawExecHandle) Kill() error { 259 if err := h.executor.ShutDown(); err != nil { 260 if h.pluginClient.Exited() { 261 return nil 262 } 263 return fmt.Errorf("executor Shutdown failed: %v", err) 264 } 265 266 select { 267 case <-h.doneCh: 268 return nil 269 case <-time.After(h.killTimeout): 270 if h.pluginClient.Exited() { 271 return nil 272 } 273 if err := h.executor.Exit(); err != nil { 274 return fmt.Errorf("executor Exit failed: %v", err) 275 } 276 277 return nil 278 } 279 } 280 281 func (h *rawExecHandle) Stats() (*cstructs.TaskResourceUsage, error) { 282 return h.executor.Stats() 283 } 284 285 func (h *rawExecHandle) run() { 286 ps, err := h.executor.Wait() 287 close(h.doneCh) 288 if ps.ExitCode == 0 && err != nil { 289 if e := killProcess(h.userPid); e != nil { 290 h.logger.Printf("[ERR] driver.raw_exec: error killing user process: %v", e) 291 } 292 if e := h.allocDir.UnmountAll(); e != nil { 293 h.logger.Printf("[ERR] driver.raw_exec: unmounting dev,proc and alloc dirs failed: %v", e) 294 } 295 } 296 h.waitCh <- &dstructs.WaitResult{ExitCode: ps.ExitCode, Signal: ps.Signal, Err: err} 297 close(h.waitCh) 298 // Remove services 299 if err := h.executor.DeregisterServices(); err != nil { 300 h.logger.Printf("[ERR] driver.raw_exec: failed to deregister services: %v", err) 301 } 302 303 if err := h.executor.Exit(); err != nil { 304 h.logger.Printf("[ERR] driver.raw_exec: error killing executor: %v", err) 305 } 306 h.pluginClient.Kill() 307 }