From 2db5b298aabff400f5dd8e9e3223eb70933c3217 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Tue, 15 Mar 2016 19:22:40 -0700 Subject: [PATCH] Make user specified tasks executable --- client/driver/executor/executor.go | 31 +++++++++++++++++++----- client/driver/executor/executor_basic.go | 16 ++++++++++++ client/driver/executor/executor_linux.go | 12 +++++++++ client/driver/java.go | 11 ++++++--- client/driver/qemu.go | 7 +++++- client/driver/rkt.go | 11 ++++++--- client/driver/utils.go | 13 ++++++++++ 7 files changed, 88 insertions(+), 13 deletions(-) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index dbed461c6..3c0596bdb 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -5,7 +5,6 @@ import ( "log" "os" "os/exec" - "path/filepath" "runtime" "strings" "sync" @@ -151,11 +150,10 @@ func (e *UniversalExecutor) LaunchCmd(command *ExecCommand, ctx *ExecutorContext e.cmd.Env = ctx.TaskEnv.EnvList() e.cmd.Path = ctx.TaskEnv.ReplaceEnv(command.Cmd) e.cmd.Args = append([]string{e.cmd.Path}, ctx.TaskEnv.ParseAndReplace(command.Args)...) - if filepath.Base(command.Cmd) == command.Cmd { - if lp, err := exec.LookPath(command.Cmd); err != nil { - } else { - e.cmd.Path = lp - } + + // Ensure that the binary being started is executable. + if err := e.makeExecutable(e.cmd.Path); err != nil { + return nil, err } // starting the process @@ -280,3 +278,24 @@ func (e *UniversalExecutor) configureTaskDir() error { e.cmd.Dir = taskDir return nil } + +// makeExecutablePosix makes the given file executable for root,group,others. +func (e *UniversalExecutor) makeExecutablePosix(binPath string) error { + fi, err := os.Stat(binPath) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("binary %q does not exist", binPath) + } + return fmt.Errorf("specified binary is invalid: %v", err) + } + + // If it is not executable, make it so. + perm := fi.Mode().Perm() + req := os.FileMode(0555) + if perm&req != req { + if err := os.Chmod(binPath, perm|req); err != nil { + return fmt.Errorf("error making %q executable: %s", binPath, err) + } + } + return nil +} diff --git a/client/driver/executor/executor_basic.go b/client/driver/executor/executor_basic.go index 9e531a547..00480e9dc 100644 --- a/client/driver/executor/executor_basic.go +++ b/client/driver/executor/executor_basic.go @@ -3,9 +3,25 @@ package executor import ( + "path/filepath" + "runtime" + cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" ) +func (e *UniversalExecutor) makeExecutable(binPath string) error { + if runtime.GOOS == "windows" { + return nil + } + + path := binPath + if !filepath.IsAbs(binPath) { + // The path must be relative the allocations directory. + path = filepath.Join(e.taskDir, binPath) + } + return e.makeExecutablePosix(path) +} + func (e *UniversalExecutor) configureChroot() error { return nil } diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index da3a89a3f..eb188c32e 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -36,6 +36,18 @@ var ( } ) +func (e *UniversalExecutor) makeExecutable(binPath string) error { + path := binPath + if e.ctx.FSIsolation { + // The path must be relative the chroot + path = filepath.Join(e.taskDir, binPath) + } else if !filepath.IsAbs(binPath) { + // The path must be relative the allocations directory. + path = filepath.Join(e.taskDir, binPath) + } + return e.makeExecutablePosix(path) +} + // configureIsolation configures chroot and creates cgroups func (e *UniversalExecutor) configureIsolation() error { if e.ctx.FSIsolation { diff --git a/client/driver/java.go b/client/driver/java.go index d3128c01a..954810e73 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -149,7 +149,7 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, Cmd: exec.Command(bin, "executor", pluginLogFile), } - exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) + execImpl, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) if err != nil { return nil, err } @@ -164,7 +164,12 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, ResourceLimits: true, } - ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: "java", Args: args}, executorCtx) + absPath, err := GetAbsolutePath("java") + if err != nil { + return nil, err + } + + ps, err := execImpl.LaunchCmd(&executor.ExecCommand{Cmd: absPath, Args: args}, executorCtx) if err != nil { pluginClient.Kill() return nil, fmt.Errorf("error starting process via the plugin: %v", err) @@ -175,7 +180,7 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, maxKill := d.DriverContext.config.MaxKillTimeout h := &javaHandle{ pluginClient: pluginClient, - executor: exec, + executor: execImpl, userPid: ps.Pid, isolationConfig: ps.IsolationConfig, taskDir: taskDir, diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 180a91006..255f85375 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -118,8 +118,13 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, // TODO: Check a lower bounds, e.g. the default 128 of Qemu mem := fmt.Sprintf("%dM", task.Resources.MemoryMB) + absPath, err := GetAbsolutePath("qemu-system-x86_64") + if err != nil { + return nil, err + } + args := []string{ - "qemu-system-x86_64", + absPath, "-machine", "type=pc,accel=" + accelerator, "-name", vmID, "-m", mem, diff --git a/client/driver/rkt.go b/client/driver/rkt.go index fa2411895..325328976 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -228,7 +228,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e Cmd: exec.Command(bin, "executor", pluginLogFile), } - exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) + execImpl, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config) if err != nil { return nil, err } @@ -241,7 +241,12 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e LogConfig: task.LogConfig, } - ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: "rkt", Args: cmdArgs}, executorCtx) + absPath, err := GetAbsolutePath("rkt") + if err != nil { + return nil, err + } + + ps, err := execImpl.LaunchCmd(&executor.ExecCommand{Cmd: absPath, Args: cmdArgs}, executorCtx) if err != nil { pluginClient.Kill() return nil, fmt.Errorf("error starting process via the plugin: %v", err) @@ -251,7 +256,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e maxKill := d.DriverContext.config.MaxKillTimeout h := &rktHandle{ pluginClient: pluginClient, - executor: exec, + executor: execImpl, executorPid: ps.Pid, allocDir: ctx.AllocDir, logger: d.logger, diff --git a/client/driver/utils.go b/client/driver/utils.go index e8921c542..1bafb8a0d 100644 --- a/client/driver/utils.go +++ b/client/driver/utils.go @@ -4,6 +4,8 @@ import ( "fmt" "io" "os" + "os/exec" + "path/filepath" "strings" "time" @@ -133,3 +135,14 @@ func GetKillTimeout(desired, max time.Duration) time.Duration { return max } + +// GetAbsolutePath returns the absolute path of the passed binary by resolving +// it in the path and following symlinks. +func GetAbsolutePath(bin string) (string, error) { + lp, err := exec.LookPath(bin) + if err != nil { + return "", fmt.Errorf("failed to resolve path to %q executable: %v", bin, err) + } + + return filepath.EvalSymlinks(lp) +}