Skip to content

Commit

Permalink
Merge pull request #380 from hashicorp/f-daemonize
Browse files Browse the repository at this point in the history
Improve spawn-daemon and Nomad Client usage of it
  • Loading branch information
dadgar committed Nov 5, 2015
2 parents ab87ef4 + 29d72b7 commit 64be746
Show file tree
Hide file tree
Showing 23 changed files with 1,263 additions and 639 deletions.
13 changes: 7 additions & 6 deletions client/driver/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ func NewExecDriver(ctx *DriverContext) Driver {
}

func (d *ExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
// Only enable if we are root when running on non-windows systems.
if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
// Only enable if we are root on linux.
if runtime.GOOS != "linux" {
d.logger.Printf("[DEBUG] driver.exec: only available on linux, disabling")
return false, nil
} else if syscall.Geteuid() != 0 {
d.logger.Printf("[DEBUG] driver.exec: must run as root user, disabling")
return false, nil
}
Expand Down Expand Up @@ -73,10 +76,8 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
}

// Add execution permissions to the newly downloaded artifact
if runtime.GOOS != "windows" {
if err := syscall.Chmod(artifactFile, 0755); err != nil {
log.Printf("[ERR] driver.Exec: Error making artifact executable: %s", err)
}
if err := syscall.Chmod(artifactFile, 0755); err != nil {
log.Printf("[ERR] driver.exec: Error making artifact executable: %s", err)
}
}

Expand Down
19 changes: 3 additions & 16 deletions client/driver/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"io/ioutil"
"path/filepath"
"reflect"
"runtime"
"testing"
"time"

Expand Down Expand Up @@ -123,13 +122,7 @@ func TestExecDriver_Start_Wait(t *testing.T) {

func TestExecDriver_Start_Artifact_basic(t *testing.T) {
ctestutils.ExecCompatible(t)
var file string
switch runtime.GOOS {
case "darwin":
file = "hi_darwin_amd64"
default:
file = "hi_linux_amd64"
}
file := "hi_linux_amd64"

task := &structs.Task{
Name: "sleep",
Expand Down Expand Up @@ -172,13 +165,7 @@ func TestExecDriver_Start_Artifact_basic(t *testing.T) {

func TestExecDriver_Start_Artifact_expanded(t *testing.T) {
ctestutils.ExecCompatible(t)
var file string
switch runtime.GOOS {
case "darwin":
file = "hi_darwin_amd64"
default:
file = "hi_linux_amd64"
}
file := "hi_linux_amd64"

task := &structs.Task{
Name: "sleep",
Expand Down Expand Up @@ -306,7 +293,7 @@ func TestExecDriver_Start_Kill_Wait(t *testing.T) {
if err == nil {
t.Fatal("should err")
}
case <-time.After(2 * time.Second):
case <-time.After(8 * time.Second):
t.Fatalf("timeout")
}
}
4 changes: 2 additions & 2 deletions client/driver/java.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ func NewJavaDriver(ctx *DriverContext) Driver {

func (d *JavaDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
// Only enable if we are root when running on non-windows systems.
if runtime.GOOS != "windows" && syscall.Geteuid() != 0 {
d.logger.Printf("[DEBUG] driver.java: must run as root user, disabling")
if runtime.GOOS == "linux" && syscall.Geteuid() != 0 {
d.logger.Printf("[DEBUG] driver.java: must run as root user on linux, disabling")
return false, nil
}

Expand Down
8 changes: 4 additions & 4 deletions client/driver/java_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func javaLocated() bool {

// The fingerprinter test should always pass, even if Java is not installed.
func TestJavaDriver_Fingerprint(t *testing.T) {
ctestutils.ExecCompatible(t)
ctestutils.JavaCompatible(t)
d := NewJavaDriver(testDriverContext(""))
node := &structs.Node{
Attributes: make(map[string]string),
Expand Down Expand Up @@ -93,7 +93,7 @@ func TestJavaDriver_Start_Wait(t *testing.T) {
t.Skip("Java not found; skipping")
}

ctestutils.ExecCompatible(t)
ctestutils.JavaCompatible(t)
task := &structs.Task{
Name: "demo-app",
Config: map[string]string{
Expand Down Expand Up @@ -141,7 +141,7 @@ func TestJavaDriver_Start_Kill_Wait(t *testing.T) {
t.Skip("Java not found; skipping")
}

ctestutils.ExecCompatible(t)
ctestutils.JavaCompatible(t)
task := &structs.Task{
Name: "demo-app",
Config: map[string]string{
Expand Down Expand Up @@ -179,7 +179,7 @@ func TestJavaDriver_Start_Kill_Wait(t *testing.T) {
if err == nil {
t.Fatal("should err")
}
case <-time.After(2 * time.Second):
case <-time.After(8 * time.Second):
t.Fatalf("timeout")
}

Expand Down
107 changes: 107 additions & 0 deletions client/executor/exec_basic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package executor

import (
"fmt"
"os"
"strconv"
"strings"

"github.com/hashicorp/nomad/client/allocdir"
"github.com/hashicorp/nomad/client/driver/args"
"github.com/hashicorp/nomad/client/driver/environment"
"github.com/hashicorp/nomad/nomad/structs"
)

// BasicExecutor should work everywhere, and as a result does not include
// any resource restrictions or runas capabilities.
type BasicExecutor struct {
cmd
}

// TODO: Update to use the Spawner.
// TODO: Have raw_exec use this as well.
func NewBasicExecutor() Executor {
return &BasicExecutor{}
}

func (e *BasicExecutor) Limit(resources *structs.Resources) error {
if resources == nil {
return errNoResources
}
return nil
}

func (e *BasicExecutor) ConfigureTaskDir(taskName string, alloc *allocdir.AllocDir) error {
taskDir, ok := alloc.TaskDirs[taskName]
if !ok {
return fmt.Errorf("Error finding task dir for (%s)", taskName)
}
e.Dir = taskDir
return nil
}

func (e *BasicExecutor) Start() error {
// Parse the commands arguments and replace instances of Nomad environment
// variables.
envVars, err := environment.ParseFromList(e.cmd.Env)
if err != nil {
return err
}

parsedPath, err := args.ParseAndReplace(e.cmd.Path, envVars.Map())
if err != nil {
return err
} else if len(parsedPath) != 1 {
return fmt.Errorf("couldn't properly parse command path: %v", e.cmd.Path)
}

e.cmd.Path = parsedPath[0]
combined := strings.Join(e.cmd.Args, " ")
parsed, err := args.ParseAndReplace(combined, envVars.Map())
if err != nil {
return err
}
e.Cmd.Args = parsed

// We don't want to call ourself. We want to call Start on our embedded Cmd
return e.cmd.Start()
}

func (e *BasicExecutor) Open(pid string) error {
pidNum, err := strconv.Atoi(pid)
if err != nil {
return fmt.Errorf("Failed to parse pid %v: %v", pid, err)
}

process, err := os.FindProcess(pidNum)
if err != nil {
return fmt.Errorf("Failed to reopen pid %d: %v", pidNum, err)
}
e.Process = process
return nil
}

func (e *BasicExecutor) Wait() error {
// We don't want to call ourself. We want to call Start on our embedded Cmd
return e.cmd.Wait()
}

func (e *BasicExecutor) ID() (string, error) {
if e.cmd.Process != nil {
return strconv.Itoa(e.cmd.Process.Pid), nil
} else {
return "", fmt.Errorf("Process has finished or was never started")
}
}

func (e *BasicExecutor) Shutdown() error {
return e.ForceStop()
}

func (e *BasicExecutor) ForceStop() error {
return e.Process.Kill()
}

func (e *BasicExecutor) Command() *cmd {
return &e.cmd
}
Loading

0 comments on commit 64be746

Please sign in to comment.