From f4bb3a9801b372d18611bdddc0b9fcaddf089319 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Sat, 19 Mar 2016 12:18:10 -0700 Subject: [PATCH] binary lookup path --- client/driver/exec.go | 2 +- client/driver/executor/executor.go | 65 ++++++++++++++++++++---- client/driver/executor/executor_basic.go | 20 +------- client/driver/executor/executor_linux.go | 12 ----- client/driver/executor/executor_test.go | 35 +++++++++++++ client/driver/java.go | 2 +- client/driver/qemu.go | 2 +- client/driver/raw_exec.go | 2 +- client/driver/rkt.go | 2 +- 9 files changed, 96 insertions(+), 46 deletions(-) diff --git a/client/driver/exec.go b/client/driver/exec.go index c832ab9de..c4598e3d8 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -112,7 +112,7 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx) if err != nil { pluginClient.Kill() - return nil, fmt.Errorf("error starting process via the plugin: %v", err) + return nil, err } d.logger.Printf("[DEBUG] driver.exec: started process via plugin with pid: %v", ps.Pid) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index 3c0596bdb..d09ec43a8 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -5,6 +5,7 @@ import ( "log" "os" "os/exec" + "path/filepath" "runtime" "strings" "sync" @@ -145,20 +146,36 @@ func (e *UniversalExecutor) LaunchCmd(command *ExecCommand, ctx *ExecutorContext e.cmd.Stderr = lre e.lre = lre - // setting the env, path and args for the command e.ctx.TaskEnv.Build() - 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)...) - // Ensure that the binary being started is executable. - if err := e.makeExecutable(e.cmd.Path); err != nil { + // Look up the binary path and make it executable + absPath, err := e.lookupBin(ctx.TaskEnv.ReplaceEnv(command.Cmd)) + if err != nil { return nil, err } - // starting the process + if err := e.makeExecutable(absPath); err != nil { + return nil, err + } + + // Determine the path to run as it may have to be relative to the chroot. + path := absPath + if e.ctx.FSIsolation { + rel, err := filepath.Rel(e.taskDir, absPath) + if err != nil { + return nil, err + } + path = rel + } + + // Set the commands arguments + e.cmd.Path = path + e.cmd.Args = append([]string{path}, ctx.TaskEnv.ParseAndReplace(command.Args)...) + e.cmd.Env = ctx.TaskEnv.EnvList() + + // Start the process if err := e.cmd.Start(); err != nil { - return nil, fmt.Errorf("error starting command: %v", err) + return nil, err } go e.wait() ic := &cstructs.IsolationConfig{Cgroup: e.groups} @@ -279,8 +296,36 @@ func (e *UniversalExecutor) configureTaskDir() error { return nil } -// makeExecutablePosix makes the given file executable for root,group,others. -func (e *UniversalExecutor) makeExecutablePosix(binPath string) error { +// lookupBin looks for path to the binary to run by looking for the binary in +// the following locations, in-order: task/local/, task/, based on host $PATH. +// The return path is absolute. +func (e *UniversalExecutor) lookupBin(bin string) (string, error) { + // Check in the local directory + local := filepath.Join(e.taskDir, allocdir.TaskLocal, bin) + if _, err := os.Stat(local); err == nil { + return local, nil + } + + // Check at the root of the task's directory + root := filepath.Join(e.taskDir, bin) + if _, err := os.Stat(root); err == nil { + return root, nil + } + + // Check the $PATH + if host, err := exec.LookPath(bin); err == nil { + return host, nil + } + + return "", fmt.Errorf("binary %q could not be found", bin) +} + +// makeExecutable makes the given file executable for root,group,others. +func (e *UniversalExecutor) makeExecutable(binPath string) error { + if runtime.GOOS == "windows" { + return nil + } + fi, err := os.Stat(binPath) if err != nil { if os.IsNotExist(err) { diff --git a/client/driver/executor/executor_basic.go b/client/driver/executor/executor_basic.go index 00480e9dc..7d59ee366 100644 --- a/client/driver/executor/executor_basic.go +++ b/client/driver/executor/executor_basic.go @@ -2,25 +2,7 @@ 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) -} +import cgroupConfig "github.com/opencontainers/runc/libcontainer/configs" func (e *UniversalExecutor) configureChroot() error { return nil diff --git a/client/driver/executor/executor_linux.go b/client/driver/executor/executor_linux.go index 17a83d5d7..8793fcf9e 100644 --- a/client/driver/executor/executor_linux.go +++ b/client/driver/executor/executor_linux.go @@ -36,18 +36,6 @@ 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/executor/executor_test.go b/client/driver/executor/executor_test.go index 2c45d0b45..f4457c161 100644 --- a/client/driver/executor/executor_test.go +++ b/client/driver/executor/executor_test.go @@ -218,3 +218,38 @@ func TestExecutor_Start_Kill(t *testing.T) { t.Fatalf("Command output incorrectly: want %v; got %v", expected, act) } } + +func TestExecutor_MakeExecutable(t *testing.T) { + // Create a temp file + f, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + defer f.Close() + defer os.Remove(f.Name()) + + // Set its permissions to be non-executable + f.Chmod(os.FileMode(0610)) + + // Make a fake exececutor + ctx := testExecutorContext(t) + defer ctx.AllocDir.Destroy() + executor := NewExecutor(log.New(os.Stdout, "", log.LstdFlags)) + + err = executor.(*UniversalExecutor).makeExecutable(f.Name()) + if err != nil { + t.Fatalf("makeExecutable() failed: %v", err) + } + + // Check the permissions + stat, err := f.Stat() + if err != nil { + t.Fatalf("Stat() failed: %v", err) + } + + act := stat.Mode().Perm() + exp := os.FileMode(0755) + if act != exp { + t.Fatalf("expected permissions %v; got %v", err) + } +} diff --git a/client/driver/java.go b/client/driver/java.go index c8849f951..378fb0667 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -172,7 +172,7 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, ps, err := execIntf.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) + return nil, err } d.logger.Printf("[DEBUG] driver.java: started process with pid: %v", ps.Pid) diff --git a/client/driver/qemu.go b/client/driver/qemu.go index 1c8d89cf5..b8894bf83 100644 --- a/client/driver/qemu.go +++ b/client/driver/qemu.go @@ -200,7 +200,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: args[0], Args: args[1:]}, executorCtx) if err != nil { pluginClient.Kill() - return nil, fmt.Errorf("error starting process via the plugin: %v", err) + return nil, err } d.logger.Printf("[INFO] Started new QemuVM: %s", vmID) diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index 3b1c87372..ea1e05fc4 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -105,7 +105,7 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx) if err != nil { pluginClient.Kill() - return nil, fmt.Errorf("error starting process via the plugin: %v", err) + return nil, err } d.logger.Printf("[DEBUG] driver.raw_exec: started process with pid: %v", ps.Pid) diff --git a/client/driver/rkt.go b/client/driver/rkt.go index e0d2a17ca..9811d2ddd 100644 --- a/client/driver/rkt.go +++ b/client/driver/rkt.go @@ -249,7 +249,7 @@ func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, e ps, err := execIntf.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) + return nil, err } d.logger.Printf("[DEBUG] driver.rkt: started ACI %q with: %v", img, cmdArgs)