Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tests for nomad/client/driver/executor package to work on Windows. #462

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 106 additions & 11 deletions client/driver/executor/test_harness_test.go
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"time" "time"


Expand All @@ -14,6 +16,88 @@ import (
"github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/nomad/structs"
) )


// testBinary is the path to the running test binary
var testBinary = os.Args[0]

func TestMain(m *testing.M) {
// The tests in this package recursively execute the test binary produced
// by go test. The TEST_MAIN environment variable controls the recursive
// execution.
switch tm := os.Getenv("TEST_MAIN"); tm {
case "":
os.Exit(m.Run())
case "app":
appMain()
default:
fmt.Fprintf(os.Stderr, "unexpected value for TEST_MAIN, \"%s\"\n", tm)
os.Exit(1)
}
}

// setTestAppEnv sets the environement of cmd for a recursive call into
// TestMain.
func setTestAppEnv(cmd *exec.Cmd) {
cmd.Env = append(os.Environ(), "TEST_MAIN=app")
}

func appMain() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "no command provided")
os.Exit(1)
}

args := os.Args[1:]

// popArg removes the first argument from args and returns it.
popArg := func() string {
s := args[0]
args = args[1:]
return s
}

// execute a sequence of operations from args
for len(args) > 0 {
switch cmd := popArg(); cmd {

case "sleep":
// sleep <dur>: sleep for a duration indicated by the first
// argument
if len(args) < 1 {
fmt.Fprintln(os.Stderr, "expected arg for sleep")
os.Exit(1)
}
dur, err := time.ParseDuration(popArg())
if err != nil {
fmt.Fprintf(os.Stderr, "could not parse sleep time: %v", err)
os.Exit(1)
}
time.Sleep(dur)

case "echo":
// echo <msg ...>: write the remaining arguments to stdout each
// separated by a single space and followed by a newline.
fmt.Println(strings.Join(args, " "))
args = args[:0]

case "write":
// write <msg> <file>: write a message to a file. The first
// argument is the msg. The second argument is the path to the
// target file.
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "expected two args for write")
os.Exit(1)
}
msg := popArg()
file := popArg()
ioutil.WriteFile(file, []byte(msg), 0666)

default:
fmt.Fprintln(os.Stderr, "unknown command:", cmd)
os.Exit(1)
}
}
}

var ( var (
constraint = &structs.Resources{ constraint = &structs.Resources{
CPU: 250, CPU: 250,
Expand Down Expand Up @@ -45,16 +129,18 @@ func testExecutor(t *testing.T, buildExecutor func() Executor, compatible func(*
} }


command := func(name string, args ...string) Executor { command := func(name string, args ...string) Executor {
b := buildExecutor() e := buildExecutor()
SetCommand(b, name, args) SetCommand(e, name, args)
return b setTestAppEnv(e.Command())
return e
} }


Executor_Start_Invalid(t, command) Executor_Start_Invalid(t, command)
Executor_Start_Wait_Failure_Code(t, command) Executor_Start_Wait_Failure_Code(t, command)
Executor_Start_Wait(t, command) Executor_Start_Wait(t, command)
Executor_Start_Kill(t, command) Executor_Start_Kill(t, command)
Executor_Open(t, command, buildExecutor) Executor_Open(t, command, buildExecutor)
Executor_Open_Invalid(t, command, buildExecutor)
} }


type buildExecCommand func(name string, args ...string) Executor type buildExecCommand func(name string, args ...string) Executor
Expand All @@ -79,7 +165,7 @@ func Executor_Start_Invalid(t *testing.T, command buildExecCommand) {
} }


func Executor_Start_Wait_Failure_Code(t *testing.T, command buildExecCommand) { func Executor_Start_Wait_Failure_Code(t *testing.T, command buildExecCommand) {
e := command("/bin/date", "-invalid") e := command(testBinary, "fail")


if err := e.Limit(constraint); err != nil { if err := e.Limit(constraint); err != nil {
log.Panicf("Limit() failed: %v", err) log.Panicf("Limit() failed: %v", err)
Expand Down Expand Up @@ -112,8 +198,7 @@ func Executor_Start_Wait(t *testing.T, command buildExecCommand) {
expected := "hello world" expected := "hello world"
file := filepath.Join(allocdir.TaskLocal, "output.txt") file := filepath.Join(allocdir.TaskLocal, "output.txt")
absFilePath := filepath.Join(taskDir, file) absFilePath := filepath.Join(taskDir, file)
cmd := fmt.Sprintf(`/bin/sleep 1 ; echo -n %v > %v`, expected, file) e := command(testBinary, "sleep", "1s", "write", expected, file)
e := command("/bin/bash", "-c", cmd)


if err := e.Limit(constraint); err != nil { if err := e.Limit(constraint); err != nil {
log.Panicf("Limit() failed: %v", err) log.Panicf("Limit() failed: %v", err)
Expand Down Expand Up @@ -152,7 +237,7 @@ func Executor_Start_Kill(t *testing.T, command buildExecCommand) {
} }


filePath := filepath.Join(taskDir, "output") filePath := filepath.Join(taskDir, "output")
e := command("/bin/bash", "-c", "sleep 1 ; echo \"failure\" > "+filePath) e := command(testBinary, "sleep", "1s", "write", "failure", filePath)


if err := e.Limit(constraint); err != nil { if err := e.Limit(constraint); err != nil {
log.Panicf("Limit() failed: %v", err) log.Panicf("Limit() failed: %v", err)
Expand Down Expand Up @@ -190,8 +275,7 @@ func Executor_Open(t *testing.T, command buildExecCommand, newExecutor func() Ex
expected := "hello world" expected := "hello world"
file := filepath.Join(allocdir.TaskLocal, "output.txt") file := filepath.Join(allocdir.TaskLocal, "output.txt")
absFilePath := filepath.Join(taskDir, file) absFilePath := filepath.Join(taskDir, file)
cmd := fmt.Sprintf(`/bin/sleep 1 ; echo -n %v > %v`, expected, file) e := command(testBinary, "sleep", "1s", "write", expected, file)
e := command("/bin/bash", "-c", cmd)


if err := e.Limit(constraint); err != nil { if err := e.Limit(constraint); err != nil {
log.Panicf("Limit() failed: %v", err) log.Panicf("Limit() failed: %v", err)
Expand Down Expand Up @@ -232,7 +316,7 @@ func Executor_Open(t *testing.T, command buildExecCommand, newExecutor func() Ex


func Executor_Open_Invalid(t *testing.T, command buildExecCommand, newExecutor func() Executor) { func Executor_Open_Invalid(t *testing.T, command buildExecCommand, newExecutor func() Executor) {
task, alloc := mockAllocDir(t) task, alloc := mockAllocDir(t)
e := command("echo", "foo") e := command(testBinary, "echo", "foo")


if err := e.Limit(constraint); err != nil { if err := e.Limit(constraint); err != nil {
log.Panicf("Limit() failed: %v", err) log.Panicf("Limit() failed: %v", err)
Expand All @@ -251,8 +335,19 @@ func Executor_Open_Invalid(t *testing.T, command buildExecCommand, newExecutor f
log.Panicf("ID() failed: %v", err) log.Panicf("ID() failed: %v", err)
} }


// Kill the task because some OSes (windows) will not let us destroy the
// alloc (below) if the task is still running.
if err := e.ForceStop(); err != nil {
log.Panicf("e.ForceStop() failed: %v", err)
}

// Wait until process is actually gone, we don't care what the result was.
e.Wait()

// Destroy the allocdir which removes the exit code. // Destroy the allocdir which removes the exit code.
alloc.Destroy() if err := alloc.Destroy(); err != nil {
log.Panicf("alloc.Destroy() failed: %v", err)
}


e2 := newExecutor() e2 := newExecutor()
if err := e2.Open(id); err == nil { if err := e2.Open(id); err == nil {
Expand Down