github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/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 cstructs "github.com/hashicorp/nomad/client/driver/structs" 17 "github.com/hashicorp/nomad/client/fingerprint" 18 "github.com/hashicorp/nomad/helper/discover" 19 "github.com/hashicorp/nomad/helper/fields" 20 "github.com/hashicorp/nomad/nomad/structs" 21 "github.com/mitchellh/mapstructure" 22 ) 23 24 const ( 25 // The option that enables this driver in the Config.Options map. 26 rawExecConfigOption = "driver.raw_exec.enable" 27 28 // The key populated in Node Attributes to indicate presence of the Raw Exec 29 // driver 30 rawExecDriverAttr = "driver.raw_exec" 31 ) 32 33 // The RawExecDriver is a privileged version of the exec driver. It provides no 34 // resource isolation and just fork/execs. The Exec driver should be preferred 35 // and this should only be used when explicitly needed. 36 type RawExecDriver struct { 37 DriverContext 38 fingerprint.StaticFingerprinter 39 } 40 41 // rawExecHandle is returned from Start/Open as a handle to the PID 42 type rawExecHandle struct { 43 version string 44 pluginClient *plugin.Client 45 userPid int 46 executor executor.Executor 47 killTimeout time.Duration 48 maxKillTimeout time.Duration 49 allocDir *allocdir.AllocDir 50 logger *log.Logger 51 waitCh chan *cstructs.WaitResult 52 doneCh chan struct{} 53 } 54 55 // NewRawExecDriver is used to create a new raw exec driver 56 func NewRawExecDriver(ctx *DriverContext) Driver { 57 return &RawExecDriver{DriverContext: *ctx} 58 } 59 60 // Validate is used to validate the driver configuration 61 func (d *RawExecDriver) Validate(config map[string]interface{}) error { 62 fd := &fields.FieldData{ 63 Raw: config, 64 Schema: map[string]*fields.FieldSchema{ 65 "command": &fields.FieldSchema{ 66 Type: fields.TypeString, 67 Required: true, 68 }, 69 "args": &fields.FieldSchema{ 70 Type: fields.TypeArray, 71 }, 72 }, 73 } 74 75 if err := fd.Validate(); err != nil { 76 return err 77 } 78 79 return nil 80 } 81 82 func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 83 // Get the current status so that we can log any debug messages only if the 84 // state changes 85 _, currentlyEnabled := node.Attributes[rawExecDriverAttr] 86 87 // Check that the user has explicitly enabled this executor. 88 enabled := cfg.ReadBoolDefault(rawExecConfigOption, false) 89 90 if enabled { 91 if currentlyEnabled { 92 d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed") 93 } 94 node.Attributes[rawExecDriverAttr] = "1" 95 return true, nil 96 } 97 98 delete(node.Attributes, rawExecDriverAttr) 99 return false, nil 100 } 101 102 func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 103 var driverConfig ExecDriverConfig 104 if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { 105 return nil, err 106 } 107 // Get the tasks local directory. 108 taskName := d.DriverContext.taskName 109 taskDir, ok := ctx.AllocDir.TaskDirs[taskName] 110 if !ok { 111 return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) 112 } 113 114 // Get the command to be ran 115 command := driverConfig.Command 116 if err := validateCommand(command, "args"); err != nil { 117 return nil, err 118 } 119 120 // Set the host environment variables. 121 filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") 122 d.taskEnv.AppendHostEnvvars(filter) 123 124 bin, err := discover.NomadExecutable() 125 if err != nil { 126 return nil, fmt.Errorf("unable to find the nomad binary: %v", err) 127 } 128 pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name)) 129 pluginConfig := &plugin.ClientConfig{ 130 Cmd: exec.Command(bin, "executor", pluginLogFile), 131 } 132 133 exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) 134 if err != nil { 135 return nil, err 136 } 137 executorCtx := &executor.ExecutorContext{ 138 TaskEnv: d.taskEnv, 139 Driver: "raw_exec", 140 AllocDir: ctx.AllocDir, 141 AllocID: ctx.AllocID, 142 Task: task, 143 } 144 145 ps, err := exec.LaunchCmd(&executor.ExecCommand{ 146 Cmd: command, 147 Args: driverConfig.Args, 148 User: task.User, 149 }, executorCtx) 150 if err != nil { 151 pluginClient.Kill() 152 return nil, err 153 } 154 d.logger.Printf("[DEBUG] driver.raw_exec: started process with pid: %v", ps.Pid) 155 156 // Return a driver handle 157 maxKill := d.DriverContext.config.MaxKillTimeout 158 h := &rawExecHandle{ 159 pluginClient: pluginClient, 160 executor: exec, 161 userPid: ps.Pid, 162 killTimeout: GetKillTimeout(task.KillTimeout, maxKill), 163 maxKillTimeout: maxKill, 164 allocDir: ctx.AllocDir, 165 version: d.config.Version, 166 logger: d.logger, 167 doneCh: make(chan struct{}), 168 waitCh: make(chan *cstructs.WaitResult, 1), 169 } 170 if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil { 171 h.logger.Printf("[ERR] driver.raw_exec: error registering services with consul for task: %q: %v", task.Name, err) 172 } 173 go h.run() 174 return h, nil 175 } 176 177 type rawExecId struct { 178 Version string 179 KillTimeout time.Duration 180 MaxKillTimeout time.Duration 181 UserPid int 182 PluginConfig *PluginReattachConfig 183 AllocDir *allocdir.AllocDir 184 } 185 186 func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 187 id := &rawExecId{} 188 if err := json.Unmarshal([]byte(handleID), id); err != nil { 189 return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err) 190 } 191 192 pluginConfig := &plugin.ClientConfig{ 193 Reattach: id.PluginConfig.PluginConfig(), 194 } 195 exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) 196 if err != nil { 197 d.logger.Println("[ERR] driver.raw_exec: error connecting to plugin so destroying plugin pid and user pid") 198 if e := destroyPlugin(id.PluginConfig.Pid, id.UserPid); e != nil { 199 d.logger.Printf("[ERR] driver.raw_exec: error destroying plugin and userpid: %v", e) 200 } 201 return nil, fmt.Errorf("error connecting to plugin: %v", err) 202 } 203 204 ver, _ := exec.Version() 205 d.logger.Printf("[DEBUG] driver.raw_exec: version of executor: %v", ver.Version) 206 207 // Return a driver handle 208 h := &rawExecHandle{ 209 pluginClient: pluginClient, 210 executor: exec, 211 userPid: id.UserPid, 212 logger: d.logger, 213 killTimeout: id.KillTimeout, 214 maxKillTimeout: id.MaxKillTimeout, 215 allocDir: id.AllocDir, 216 version: id.Version, 217 doneCh: make(chan struct{}), 218 waitCh: make(chan *cstructs.WaitResult, 1), 219 } 220 if err := h.executor.SyncServices(consulContext(d.config, "")); err != nil { 221 h.logger.Printf("[ERR] driver.raw_exec: error registering services with consul: %v", err) 222 } 223 go h.run() 224 return h, nil 225 } 226 227 func (h *rawExecHandle) ID() string { 228 id := rawExecId{ 229 Version: h.version, 230 KillTimeout: h.killTimeout, 231 MaxKillTimeout: h.maxKillTimeout, 232 PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()), 233 UserPid: h.userPid, 234 AllocDir: h.allocDir, 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 *cstructs.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) Kill() error { 258 if err := h.executor.ShutDown(); err != nil { 259 if h.pluginClient.Exited() { 260 return nil 261 } 262 return fmt.Errorf("executor Shutdown failed: %v", err) 263 } 264 265 select { 266 case <-h.doneCh: 267 return nil 268 case <-time.After(h.killTimeout): 269 if h.pluginClient.Exited() { 270 return nil 271 } 272 if err := h.executor.Exit(); err != nil { 273 return fmt.Errorf("executor Exit failed: %v", err) 274 } 275 276 return nil 277 } 278 } 279 280 func (h *rawExecHandle) run() { 281 ps, err := h.executor.Wait() 282 close(h.doneCh) 283 if ps.ExitCode == 0 && err != nil { 284 if e := killProcess(h.userPid); e != nil { 285 h.logger.Printf("[ERR] driver.raw_exec: error killing user process: %v", e) 286 } 287 if e := h.allocDir.UnmountAll(); e != nil { 288 h.logger.Printf("[ERR] driver.raw_exec: unmounting dev,proc and alloc dirs failed: %v", e) 289 } 290 } 291 h.waitCh <- &cstructs.WaitResult{ExitCode: ps.ExitCode, Signal: ps.Signal, Err: err} 292 close(h.waitCh) 293 // Remove services 294 if err := h.executor.DeregisterServices(); err != nil { 295 h.logger.Printf("[ERR] driver.raw_exec: failed to deregister services: %v", err) 296 } 297 298 if err := h.executor.Exit(); err != nil { 299 h.logger.Printf("[ERR] driver.raw_exec: error killing executor: %v", err) 300 } 301 h.pluginClient.Kill() 302 }