Skip to content

Commit

Permalink
Add test and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
dadgar committed May 30, 2018
1 parent c8b7487 commit 448ac9c
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 3 deletions.
2 changes: 1 addition & 1 deletion client/driver/raw_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (d *RawExecDriver) Fingerprint(req *cstructs.FingerprintRequest, resp *cstr

func (d *RawExecDriver) Prestart(*ExecContext, *structs.Task) (*PrestartResponse, error) {
// If we are on linux, running as root, cgroups are mounted, and cgroups
// aren't disabled by the operate use cgroups for pid management.
// aren't disabled by the operator use cgroups for pid management.
forceDisable := d.DriverContext.config.ReadBoolDefault(rawExecNoCgroupOption, false)
if !forceDisable && runtime.GOOS == "linux" &&
syscall.Geteuid() == 0 && cgroupsMounted(d.DriverContext.node) {
Expand Down
90 changes: 90 additions & 0 deletions client/driver/raw_exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strconv"
"syscall"
"testing"
"time"

"github.com/hashicorp/nomad/client/config"
"github.com/hashicorp/nomad/client/driver/env"
cstructs "github.com/hashicorp/nomad/client/structs"
tu "github.com/hashicorp/nomad/client/testutil"
"github.com/hashicorp/nomad/helper/testtask"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
Expand Down Expand Up @@ -261,6 +265,92 @@ func TestRawExecDriver_Start_Kill_Wait(t *testing.T) {
}
}

func TestRawExecDriver_Start_Kill_Wait_Cgroup(t *testing.T) {
tu.ExecCompatible(t)
t.Parallel()
pidFile := "pid"
task := &structs.Task{
Name: "sleep",
Driver: "raw_exec",
Config: map[string]interface{}{
"command": testtask.Path(),
"args": []string{"fork/exec", pidFile, "pgrp", "0", "sleep", "20s"},
},
LogConfig: &structs.LogConfig{
MaxFiles: 10,
MaxFileSizeMB: 10,
},
Resources: basicResources,
User: "root",
}
testtask.SetTaskEnv(task)

ctx := testDriverContexts(t, task)
ctx.DriverCtx.node.Attributes["unique.cgroup.mountpoint"] = "foo" // Enable cgroups
defer ctx.AllocDir.Destroy()
d := NewRawExecDriver(ctx.DriverCtx)

if _, err := d.Prestart(ctx.ExecCtx, task); err != nil {
t.Fatalf("prestart err: %v", err)
}
resp, err := d.Start(ctx.ExecCtx, task)
if err != nil {
t.Fatalf("err: %v", err)
}

// Find the process
var pidData []byte
testutil.WaitForResult(func() (bool, error) {
var err error
pidData, err = ioutil.ReadFile(filepath.Join(ctx.AllocDir.AllocDir, "sleep", pidFile))
if err != nil {
return false, err
}

return true, nil
}, func(err error) {
t.Fatalf("err: %v", err)
})

pid, err := strconv.Atoi(string(pidData))
if err != nil {
t.Fatalf("failed to convert pid: %v", err)
}

// Check the pid is up
process, err := os.FindProcess(pid)
if err != nil {
t.Fatalf("failed to find process")
}
if err := process.Signal(syscall.Signal(0)); err != nil {
t.Fatalf("process doesn't exist: %v", err)
}

go func() {
time.Sleep(1 * time.Second)
err := resp.Handle.Kill()

// Can't rely on the ordering between wait and kill on travis...
if !testutil.IsTravis() && err != nil {
t.Fatalf("err: %v", err)
}
}()

// Task should terminate quickly
select {
case res := <-resp.Handle.WaitCh():
if res.Successful() {
t.Fatal("should err")
}
case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second):
t.Fatalf("timeout")
}

if err := process.Signal(syscall.Signal(0)); err == nil {
t.Fatalf("process should not exist: %v", pid)
}
}

func TestRawExecDriver_HandlerExec(t *testing.T) {
t.Parallel()
task := &structs.Task{
Expand Down
2 changes: 0 additions & 2 deletions client/fingerprint/cgroup.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// +build linux

package fingerprint

import (
Expand Down
15 changes: 15 additions & 0 deletions client/fingerprint/cgroup_default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// +build !linux

package fingerprint

import cstructs "github.com/hashicorp/nomad/client/structs"

// FindCgroupMountpointDir is used to find the cgroup mount point on a Linux
// system. Here it is a no-op implemtation
func FindCgroupMountpointDir() (string, error) {
return "", nil
}

func (f *CGroupFingerprint) Fingerprint(*cstructs.FingerprintRequest, *cstructs.FingerprintResponse) error {
return nil
}
10 changes: 10 additions & 0 deletions client/testutil/driver_compatible.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"sync"
"syscall"
"testing"

"github.com/hashicorp/nomad/client/fingerprint"
)

// RequireRoot skips tests unless running on a Unix as root.
Expand All @@ -19,6 +21,7 @@ func ExecCompatible(t *testing.T) {
if runtime.GOOS != "linux" || syscall.Geteuid() != 0 {
t.Skip("Test only available running as root on linux")
}
CgroupCompatible(t)
}

func JavaCompatible(t *testing.T) {
Expand All @@ -39,6 +42,13 @@ func QemuCompatible(t *testing.T) {
}
}

func CgroupCompatible(t *testing.T) {
mount, err := fingerprint.FindCgroupMountpointDir()
if err != nil || mount == "" {
t.Skipf("Failed to find cgroup mount: %v %v", mount, err)
}
}

var rktExists bool
var rktOnce sync.Once

Expand Down
46 changes: 46 additions & 0 deletions helper/testtask/testtask.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"io/ioutil"
"os"
"os/exec"
"strconv"
"syscall"
"time"

"github.com/hashicorp/nomad/nomad/structs"
Expand Down Expand Up @@ -103,6 +105,50 @@ func execute() {
file := popArg()
ioutil.WriteFile(file, []byte(msg), 0666)

case "pgrp":
// pgrp <group_int> puts the pid in a new process group
if len(args) < 1 {
fmt.Fprintln(os.Stderr, "expected process group number for pgrp")
os.Exit(1)
}
num := popArg()
grp, err := strconv.Atoi(num)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to convert process group number %q: %v\n", num, err)
os.Exit(1)
}
if err := syscall.Setpgid(0, grp); err != nil {
fmt.Fprintf(os.Stderr, "failed to set process group: %v\n", err)
os.Exit(1)
}

case "fork/exec":
// fork/exec <pid_file> <args> forks execs the helper process
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "expect pid file and remaining args to fork exec")
os.Exit(1)
}

pidFile := popArg()

cmd := exec.Command(Path(), args...)
SetCmdEnv(cmd)
if err := cmd.Start(); err != nil {
fmt.Fprintf(os.Stderr, "failed to fork/exec: %v\n", err)
os.Exit(1)
}

if err := ioutil.WriteFile(pidFile, []byte(fmt.Sprintf("%d", cmd.Process.Pid)), 777); err != nil {
fmt.Fprintf(os.Stderr, "failed to write pid file: %v\n", err)
os.Exit(1)
}

if err := cmd.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "wait failed: %v\n", err)
os.Exit(1)
}
return

default:
fmt.Fprintln(os.Stderr, "unknown command:", cmd)
os.Exit(1)
Expand Down
11 changes: 11 additions & 0 deletions website/source/docs/drivers/raw_exec.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ client {
}
```

## Client Options

* `driver.raw_exec.enable` - Specifies whether the driver should be enabled or
disabled.

* `driver.raw_exec.no_cgroups` - Specifies whether the driver should not use
cgroups to manage the process group launched by the driver. By default,
cgroups are used to manage the process tree to ensure full cleanup of all
processes started by the task. The driver only uses cgroups when Nomad is
launched as root, on Linux and when cgroups are detected.

## Client Attributes

The `raw_exec` driver will set the following client attributes:
Expand Down

0 comments on commit 448ac9c

Please sign in to comment.