Merge pull request #442 from ChrisHines/windows-spawn

Fix tests for nomad/client/driver/spawn package to work on Windows.
This commit is contained in:
Alex Dadgar 2015-11-20 14:49:54 -08:00
commit 34b2f62c0b
2 changed files with 124 additions and 111 deletions

View File

@ -73,10 +73,10 @@ func (s *Spawner) Spawn(cb func(pid int) error) error {
}
exitFile, err := os.OpenFile(s.StateFile, os.O_CREATE|os.O_WRONLY, 0666)
defer exitFile.Close()
if err != nil {
return fmt.Errorf("Error opening file to store exit status: %v", err)
}
defer exitFile.Close()
config, err := s.spawnConfig()
if err != nil {
@ -87,17 +87,17 @@ func (s *Spawner) Spawn(cb func(pid int) error) error {
// Capture stdout
spawnStdout, err := spawn.StdoutPipe()
defer spawnStdout.Close()
if err != nil {
return fmt.Errorf("Failed to capture spawn-daemon stdout: %v", err)
}
defer spawnStdout.Close()
// Capture stdin.
spawnStdin, err := spawn.StdinPipe()
defer spawnStdin.Close()
if err != nil {
return fmt.Errorf("Failed to capture spawn-daemon stdin: %v", err)
}
defer spawnStdin.Close()
if err := spawn.Start(); err != nil {
return fmt.Errorf("Failed to call spawn-daemon on nomad executable: %v", err)
@ -264,10 +264,10 @@ func (s *Spawner) pollWait() *structs.WaitResult {
// returns an error if the file can't be read.
func (s *Spawner) readExitCode() *structs.WaitResult {
f, err := os.Open(s.StateFile)
defer f.Close()
if err != nil {
return structs.NewWaitResult(-1, 0, fmt.Errorf("Failed to open %v to read exit code: %v", s.StateFile, err))
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
@ -289,7 +289,7 @@ func (s *Spawner) readExitCode() *structs.WaitResult {
// Valid checks that the state of the Spawner is valid and that a subsequent
// Wait could be called. This is useful to call when reopening a Spawner
// throught client restarts. If Valid a nil error is returned.
// through client restarts. If Valid a nil error is returned.
func (s *Spawner) Valid() error {
// If the spawner is still alive, then the task is running and we can wait
// on it.

View File

@ -5,64 +5,81 @@ import (
"io/ioutil"
"os"
"os/exec"
"runtime"
"strings"
"testing"
"time"
)
func TestSpawn_NoCmd(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
func TestMain(m *testing.M) {
switch os.Getenv("TEST_MAIN") {
case "app":
appMain()
default:
os.Exit(m.Run())
}
defer os.Remove(f.Name())
}
spawn := NewSpawner(f.Name())
func appMain() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "no command provided")
os.Exit(1)
}
switch cmd := os.Args[1]; cmd {
case "echo":
fmt.Println(strings.Join(os.Args[2:], " "))
case "sleep":
if len(os.Args) != 3 {
fmt.Fprintln(os.Stderr, "expected 3 args")
os.Exit(1)
}
dur, err := time.ParseDuration(os.Args[2])
if err != nil {
fmt.Fprintf(os.Stderr, "could not parse sleep time: %v", err)
os.Exit(1)
}
time.Sleep(dur)
default:
fmt.Fprintln(os.Stderr, "unknown command:", cmd)
os.Exit(1)
}
}
func TestSpawn_NoCmd(t *testing.T) {
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(tempFile)
if err := spawn.Spawn(nil); err == nil {
t.Fatalf("Spawn() with no user command should fail")
}
}
func TestSpawn_InvalidCmd(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("foo"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(exec.Command("foo")) // non-existent command
if err := spawn.Spawn(nil); err == nil {
t.Fatalf("Spawn() with no invalid command should fail")
t.Fatalf("Spawn() with an invalid command should fail")
}
}
func TestSpawn_SetsLogs(t *testing.T) {
// TODO: Figure out why this test fails. If the spawn-daemon directly writes
// to the opened stdout file it works but not the user command. Maybe a
// flush issue?
if runtime.GOOS == "windows" {
t.Skip("Test fails on windows; unknown reason. Skipping")
}
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
spawn := NewSpawner(f.Name())
spawn := NewSpawner(tempFile)
exp := "foo"
spawn.SetCommand(exec.Command("echo", exp))
spawn.SetCommand(testCommand("echo", exp))
// Create file for stdout.
stdout, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(stdout.Name())
spawn.SetLogs(&Logs{Stdout: stdout.Name()})
stdout := tempFileName(t)
defer os.Remove(stdout)
spawn.SetLogs(&Logs{Stdout: stdout})
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed: %v", err)
@ -72,7 +89,7 @@ func TestSpawn_SetsLogs(t *testing.T) {
t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err)
}
stdout2, err := os.Open(stdout.Name())
stdout2, err := os.Open(stdout)
if err != nil {
t.Fatalf("Open() failed: %v", err)
}
@ -89,14 +106,12 @@ func TestSpawn_SetsLogs(t *testing.T) {
}
func TestSpawn_Callback(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("sleep", "1"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("sleep", "1s"))
called := false
cbErr := fmt.Errorf("ERROR CB")
@ -115,14 +130,12 @@ func TestSpawn_Callback(t *testing.T) {
}
func TestSpawn_ParentWaitExited(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("echo", "foo"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("echo", "foo"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -135,14 +148,12 @@ func TestSpawn_ParentWaitExited(t *testing.T) {
}
func TestSpawn_ParentWait(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("sleep", "2"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("sleep", "2s"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -153,14 +164,12 @@ func TestSpawn_ParentWait(t *testing.T) {
}
func TestSpawn_NonParentWaitExited(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("echo", "foo"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("echo", "foo"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -175,14 +184,12 @@ func TestSpawn_NonParentWaitExited(t *testing.T) {
}
func TestSpawn_NonParentWait(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("sleep", "2"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("sleep", "2s"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -204,11 +211,9 @@ func TestSpawn_NonParentWait(t *testing.T) {
}
func TestSpawn_DeadSpawnDaemon_Parent(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
var spawnPid int
cb := func(pid int) error {
@ -216,8 +221,8 @@ func TestSpawn_DeadSpawnDaemon_Parent(t *testing.T) {
return nil
}
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("sleep", "5"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("sleep", "5s"))
if err := spawn.Spawn(cb); err != nil {
t.Fatalf("Spawn() errored: %v", err)
}
@ -241,11 +246,9 @@ func TestSpawn_DeadSpawnDaemon_Parent(t *testing.T) {
}
func TestSpawn_DeadSpawnDaemon_NonParent(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
var spawnPid int
cb := func(pid int) error {
@ -253,8 +256,8 @@ func TestSpawn_DeadSpawnDaemon_NonParent(t *testing.T) {
return nil
}
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("sleep", "2"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("sleep", "2s"))
if err := spawn.Spawn(cb); err != nil {
t.Fatalf("Spawn() errored: %v", err)
}
@ -280,14 +283,12 @@ func TestSpawn_DeadSpawnDaemon_NonParent(t *testing.T) {
}
func TestSpawn_Valid_TaskRunning(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("sleep", "2"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("sleep", "2s"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -302,14 +303,12 @@ func TestSpawn_Valid_TaskRunning(t *testing.T) {
}
func TestSpawn_Valid_TaskExit_ExitCode(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer os.Remove(f.Name())
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("echo", "foo"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("echo", "foo"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -324,13 +323,12 @@ func TestSpawn_Valid_TaskExit_ExitCode(t *testing.T) {
}
func TestSpawn_Valid_TaskExit_NoExitCode(t *testing.T) {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
t.Parallel()
tempFile := tempFileName(t)
defer os.Remove(tempFile)
spawn := NewSpawner(f.Name())
spawn.SetCommand(exec.Command("echo", "foo"))
spawn := NewSpawner(tempFile)
spawn.SetCommand(testCommand("echo", "foo"))
if err := spawn.Spawn(nil); err != nil {
t.Fatalf("Spawn() failed %v", err)
}
@ -340,9 +338,24 @@ func TestSpawn_Valid_TaskExit_NoExitCode(t *testing.T) {
}
// Delete the file so that it can't find the exit code.
os.Remove(f.Name())
os.Remove(tempFile)
if err := spawn.Valid(); err == nil {
t.Fatalf("Valid() should have failed")
}
}
func tempFileName(t *testing.T) string {
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile() failed")
}
defer f.Close()
return f.Name()
}
func testCommand(args ...string) *exec.Cmd {
cmd := exec.Command(os.Args[0], args...)
cmd.Env = append(os.Environ(), "TEST_MAIN=app")
return cmd
}