diff --git a/appveyor.yml b/appveyor.yml index 6f738e20b..d54b0bf06 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,11 +3,17 @@ image: Visual Studio 2017 clone_folder: c:\gopath\src\github.com\hashicorp\nomad environment: GOPATH: c:\gopath + GOBIN: c:\gopath\bin install: +- cmd: set PATH=%GOBIN%;c:\go\bin;%PATH% - cmd: echo %Path% - cmd: go version - cmd: go env -- cmd: go get github.com/hashicorp/vault -- cmd: go install +- ps: mkdir C:\gopath\bin +- ps: appveyor DownloadFile "https://releases.hashicorp.com/vault/0.7.0/vault_0.7.0_windows_amd64.zip" -FileName "C:\\gopath\\bin\\vault.zip" +- ps: Expand-Archive C:\gopath\bin\vault.zip -DestinationPath C:\gopath\bin +- ps: appveyor DownloadFile "https://releases.hashicorp.com/consul/0.7.0/consul_0.7.0_windows_amd64.zip" -FileName "C:\\gopath\\bin\\consul.zip" +- ps: Expand-Archive C:\gopath\bin\consul.zip -DestinationPath C:\gopath\bin +- cmd: go install -tags nomad_test build_script: - cmd: go test -tags nomad_test ./... diff --git a/client/driver/docker_test.go b/client/driver/docker_test.go index 8a1f439bd..e3fdabbd8 100644 --- a/client/driver/docker_test.go +++ b/client/driver/docker_test.go @@ -10,7 +10,6 @@ import ( "runtime/debug" "strconv" "strings" - "syscall" "testing" "time" @@ -1048,95 +1047,6 @@ func TestDockerDriver_Stats(t *testing.T) { } } -func TestDockerDriver_Signal(t *testing.T) { - if !testutil.DockerIsConnected(t) { - t.SkipNow() - } - - task := &structs.Task{ - Name: "redis-demo", - Driver: "docker", - Config: map[string]interface{}{ - "image": "busybox", - "load": "busybox.tar", - "command": "/bin/sh", - "args": []string{"local/test.sh"}, - }, - Resources: &structs.Resources{ - MemoryMB: 256, - CPU: 512, - }, - LogConfig: &structs.LogConfig{ - MaxFiles: 10, - MaxFileSizeMB: 10, - }, - } - - ctx := testDockerDriverContexts(t, task) - //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} - defer ctx.AllocDir.Destroy() - d := NewDockerDriver(ctx.DriverCtx) - - // Copy the image into the task's directory - copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") - - testFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "test.sh") - testData := []byte(` -at_term() { - echo 'Terminated.' - exit 3 -} -trap at_term USR1 -while true; do - sleep 1 -done - `) - if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { - t.Fatalf("Failed to write data: %v", err) - } - - _, err := d.Prestart(ctx.ExecCtx, task) - if err != nil { - t.Fatalf("error in prestart: %v", err) - } - handle, err := d.Start(ctx.ExecCtx, task) - if err != nil { - t.Fatalf("err: %v", err) - } - if handle == nil { - t.Fatalf("missing handle") - } - defer handle.Kill() - - waitForExist(t, handle.(*DockerHandle).client, handle.(*DockerHandle)) - - time.Sleep(1 * time.Second) - if err := handle.Signal(syscall.SIGUSR1); err != nil { - t.Fatalf("Signal returned an error: %v", err) - } - - select { - case res := <-handle.WaitCh(): - if res.Successful() { - t.Fatalf("should err: %v", res) - } - case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): - t.Fatalf("timeout") - } - - // Check the log file to see it exited because of the signal - outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "redis-demo.stdout.0") - act, err := ioutil.ReadFile(outputFile) - if err != nil { - t.Fatalf("Couldn't read expected output: %v", err) - } - - exp := "Terminated." - if strings.TrimSpace(string(act)) != exp { - t.Fatalf("Command outputted %v; want %v", act, exp) - } -} - func setupDockerVolumes(t *testing.T, cfg *config.Config, hostpath string) (*structs.Task, Driver, *ExecContext, string, func()) { if !testutil.DockerIsConnected(t) { t.SkipNow() diff --git a/client/driver/docker_unix_test.go b/client/driver/docker_unix_test.go new file mode 100644 index 000000000..ca9f5b1de --- /dev/null +++ b/client/driver/docker_unix_test.go @@ -0,0 +1,105 @@ +// +build !windows + +package driver + +import ( + "io/ioutil" + "path/filepath" + "strings" + "syscall" + "testing" + "time" + + "github.com/hashicorp/nomad/client/testutil" + "github.com/hashicorp/nomad/nomad/structs" + tu "github.com/hashicorp/nomad/testutil" +) + +func TestDockerDriver_Signal(t *testing.T) { + if !testutil.DockerIsConnected(t) { + t.SkipNow() + } + + task := &structs.Task{ + Name: "redis-demo", + Driver: "docker", + Config: map[string]interface{}{ + "image": "busybox", + "load": "busybox.tar", + "command": "/bin/sh", + "args": []string{"local/test.sh"}, + }, + Resources: &structs.Resources{ + MemoryMB: 256, + CPU: 512, + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + } + + ctx := testDockerDriverContexts(t, task) + //ctx.DriverCtx.config.Options = map[string]string{"docker.cleanup.image": "false"} + defer ctx.AllocDir.Destroy() + d := NewDockerDriver(ctx.DriverCtx) + + // Copy the image into the task's directory + copyImage(t, ctx.ExecCtx.TaskDir, "busybox.tar") + + testFile := filepath.Join(ctx.ExecCtx.TaskDir.LocalDir, "test.sh") + testData := []byte(` +at_term() { + echo 'Terminated.' + exit 3 +} +trap at_term USR1 +while true; do + sleep 1 +done + `) + if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { + t.Fatalf("Failed to write data: %v", err) + } + + _, err := d.Prestart(ctx.ExecCtx, task) + if err != nil { + t.Fatalf("error in prestart: %v", err) + } + handle, err := d.Start(ctx.ExecCtx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + defer handle.Kill() + + waitForExist(t, handle.(*DockerHandle).client, handle.(*DockerHandle)) + + time.Sleep(1 * time.Second) + if err := handle.Signal(syscall.SIGUSR1); err != nil { + t.Fatalf("Signal returned an error: %v", err) + } + + select { + case res := <-handle.WaitCh(): + if res.Successful() { + t.Fatalf("should err: %v", res) + } + case <-time.After(time.Duration(tu.TestMultiplier()*5) * time.Second): + t.Fatalf("timeout") + } + + // Check the log file to see it exited because of the signal + outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "redis-demo.stdout.0") + act, err := ioutil.ReadFile(outputFile) + if err != nil { + t.Fatalf("Couldn't read expected output: %v", err) + } + + exp := "Terminated." + if strings.TrimSpace(string(act)) != exp { + t.Fatalf("Command outputted %v; want %v", act, exp) + } +} diff --git a/client/driver/exec_test.go b/client/driver/exec_test.go index 6546e714e..85976df1a 100644 --- a/client/driver/exec_test.go +++ b/client/driver/exec_test.go @@ -1,14 +1,11 @@ package driver import ( - "encoding/json" "fmt" "io/ioutil" - "os" "path/filepath" "reflect" "strings" - "syscall" "testing" "time" @@ -90,79 +87,6 @@ func TestExecDriver_StartOpen_Wait(t *testing.T) { handle.Kill() handle2.Kill() } - -func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) { - ctestutils.ExecCompatible(t) - task := &structs.Task{ - Name: "sleep", - Driver: "exec", - Config: map[string]interface{}{ - "command": "/bin/sleep", - "args": []string{"1000000"}, - }, - LogConfig: &structs.LogConfig{ - MaxFiles: 10, - MaxFileSizeMB: 10, - }, - Resources: basicResources, - } - - ctx := testDriverContexts(t, task) - defer ctx.AllocDir.Destroy() - d := NewExecDriver(ctx.DriverCtx) - - if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { - t.Fatalf("prestart err: %v", err) - } - handle, err := d.Start(ctx.ExecCtx, task) - if err != nil { - t.Fatalf("err: %v", err) - } - if handle == nil { - t.Fatalf("missing handle") - } - defer handle.Kill() - - id := &execId{} - if err := json.Unmarshal([]byte(handle.ID()), id); err != nil { - t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err) - } - pluginPid := id.PluginConfig.Pid - proc, err := os.FindProcess(pluginPid) - if err != nil { - t.Fatalf("can't find plugin pid: %v", pluginPid) - } - if err := proc.Kill(); err != nil { - t.Fatalf("can't kill plugin pid: %v", err) - } - - // Attempt to open - handle2, err := d.Open(ctx.ExecCtx, handle.ID()) - if err == nil { - t.Fatalf("expected error") - } - if handle2 != nil { - handle2.Kill() - t.Fatalf("expected handle2 to be nil") - } - - // Test if the userpid is still present - userProc, _ := os.FindProcess(id.UserPid) - - for retry := 3; retry > 0; retry-- { - if err = userProc.Signal(syscall.Signal(0)); err != nil { - // Process is gone as expected; exit - return - } - - // Killing processes is async; wait and check again - time.Sleep(time.Second) - } - if err = userProc.Signal(syscall.Signal(0)); err == nil { - t.Fatalf("expected user process to die") - } -} - func TestExecDriver_Start_Wait(t *testing.T) { ctestutils.ExecCompatible(t) task := &structs.Task{ @@ -321,85 +245,6 @@ func TestExecDriver_Start_Kill_Wait(t *testing.T) { } } -func TestExecDriver_Signal(t *testing.T) { - ctestutils.ExecCompatible(t) - task := &structs.Task{ - Name: "signal", - Driver: "exec", - Config: map[string]interface{}{ - "command": "/bin/bash", - "args": []string{"test.sh"}, - }, - LogConfig: &structs.LogConfig{ - MaxFiles: 10, - MaxFileSizeMB: 10, - }, - Resources: basicResources, - KillTimeout: 10 * time.Second, - } - - ctx := testDriverContexts(t, task) - defer ctx.AllocDir.Destroy() - d := NewExecDriver(ctx.DriverCtx) - - testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh") - testData := []byte(` -at_term() { - echo 'Terminated.' - exit 3 -} -trap at_term USR1 -while true; do - sleep 1 -done - `) - if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { - t.Fatalf("Failed to write data: %v", err) - } - - if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { - t.Fatalf("prestart err: %v", err) - } - handle, err := d.Start(ctx.ExecCtx, task) - if err != nil { - t.Fatalf("err: %v", err) - } - if handle == nil { - t.Fatalf("missing handle") - } - - go func() { - time.Sleep(100 * time.Millisecond) - err := handle.Signal(syscall.SIGUSR1) - if err != nil { - t.Fatalf("err: %v", err) - } - }() - - // Task should terminate quickly - select { - case res := <-handle.WaitCh(): - if res.Successful() { - t.Fatal("should err") - } - case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second): - t.Fatalf("timeout") - } - - // Check the log file to see it exited because of the signal - outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0") - act, err := ioutil.ReadFile(outputFile) - if err != nil { - t.Fatalf("Couldn't read expected output: %v", err) - } - - exp := "Terminated." - if strings.TrimSpace(string(act)) != exp { - t.Logf("Read from %v", outputFile) - t.Fatalf("Command outputted %v; want %v", act, exp) - } -} - func TestExecDriverUser(t *testing.T) { ctestutils.ExecCompatible(t) task := &structs.Task{ diff --git a/client/driver/exec_unix_test.go b/client/driver/exec_unix_test.go new file mode 100644 index 000000000..e07d7badd --- /dev/null +++ b/client/driver/exec_unix_test.go @@ -0,0 +1,170 @@ +// +build !windows + +package driver + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "strings" + "syscall" + "testing" + "time" + + "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/testutil" + + ctestutils "github.com/hashicorp/nomad/client/testutil" +) + +func TestExecDriver_KillUserPid_OnPluginReconnectFailure(t *testing.T) { + ctestutils.ExecCompatible(t) + task := &structs.Task{ + Name: "sleep", + Driver: "exec", + Config: map[string]interface{}{ + "command": "/bin/sleep", + "args": []string{"1000000"}, + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + Resources: basicResources, + } + + ctx := testDriverContexts(t, task) + defer ctx.AllocDir.Destroy() + d := NewExecDriver(ctx.DriverCtx) + + if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { + t.Fatalf("prestart err: %v", err) + } + handle, err := d.Start(ctx.ExecCtx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + defer handle.Kill() + + id := &execId{} + if err := json.Unmarshal([]byte(handle.ID()), id); err != nil { + t.Fatalf("Failed to parse handle '%s': %v", handle.ID(), err) + } + pluginPid := id.PluginConfig.Pid + proc, err := os.FindProcess(pluginPid) + if err != nil { + t.Fatalf("can't find plugin pid: %v", pluginPid) + } + if err := proc.Kill(); err != nil { + t.Fatalf("can't kill plugin pid: %v", err) + } + + // Attempt to open + handle2, err := d.Open(ctx.ExecCtx, handle.ID()) + if err == nil { + t.Fatalf("expected error") + } + if handle2 != nil { + handle2.Kill() + t.Fatalf("expected handle2 to be nil") + } + + // Test if the userpid is still present + userProc, _ := os.FindProcess(id.UserPid) + + for retry := 3; retry > 0; retry-- { + if err = userProc.Signal(syscall.Signal(0)); err != nil { + // Process is gone as expected; exit + return + } + + // Killing processes is async; wait and check again + time.Sleep(time.Second) + } + if err = userProc.Signal(syscall.Signal(0)); err == nil { + t.Fatalf("expected user process to die") + } +} + +func TestExecDriver_Signal(t *testing.T) { + ctestutils.ExecCompatible(t) + task := &structs.Task{ + Name: "signal", + Driver: "exec", + Config: map[string]interface{}{ + "command": "/bin/bash", + "args": []string{"test.sh"}, + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + Resources: basicResources, + KillTimeout: 10 * time.Second, + } + + ctx := testDriverContexts(t, task) + defer ctx.AllocDir.Destroy() + d := NewExecDriver(ctx.DriverCtx) + + testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh") + testData := []byte(` +at_term() { + echo 'Terminated.' + exit 3 +} +trap at_term USR1 +while true; do + sleep 1 +done + `) + if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { + t.Fatalf("Failed to write data: %v", err) + } + + if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { + t.Fatalf("prestart err: %v", err) + } + handle, err := d.Start(ctx.ExecCtx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + + go func() { + time.Sleep(100 * time.Millisecond) + err := handle.Signal(syscall.SIGUSR1) + if err != nil { + t.Fatalf("err: %v", err) + } + }() + + // Task should terminate quickly + select { + case res := <-handle.WaitCh(): + if res.Successful() { + t.Fatal("should err") + } + case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second): + t.Fatalf("timeout") + } + + // Check the log file to see it exited because of the signal + outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0") + act, err := ioutil.ReadFile(outputFile) + if err != nil { + t.Fatalf("Couldn't read expected output: %v", err) + } + + exp := "Terminated." + if strings.TrimSpace(string(act)) != exp { + t.Logf("Read from %v", outputFile) + t.Fatalf("Command outputted %v; want %v", act, exp) + } +} diff --git a/client/driver/raw_exec_test.go b/client/driver/raw_exec_test.go index 78ea29160..7b96c2218 100644 --- a/client/driver/raw_exec_test.go +++ b/client/driver/raw_exec_test.go @@ -6,7 +6,6 @@ import ( "path/filepath" "reflect" "strings" - "syscall" "testing" "time" @@ -299,81 +298,3 @@ func TestRawExecDriverUser(t *testing.T) { t.Fatalf("Expecting '%v' in '%v'", msg, err) } } - -func TestRawExecDriver_Signal(t *testing.T) { - task := &structs.Task{ - Name: "signal", - Driver: "raw_exec", - Config: map[string]interface{}{ - "command": "/bin/bash", - "args": []string{"test.sh"}, - }, - LogConfig: &structs.LogConfig{ - MaxFiles: 10, - MaxFileSizeMB: 10, - }, - Resources: basicResources, - KillTimeout: 10 * time.Second, - } - - ctx := testDriverContexts(t, task) - defer ctx.AllocDir.Destroy() - d := NewRawExecDriver(ctx.DriverCtx) - - testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh") - testData := []byte(` -at_term() { - echo 'Terminated.' - exit 3 -} -trap at_term USR1 -while true; do - sleep 1 -done - `) - if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { - t.Fatalf("Failed to write data: %v", err) - } - - if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { - t.Fatalf("prestart err: %v", err) - } - handle, err := d.Start(ctx.ExecCtx, task) - if err != nil { - t.Fatalf("err: %v", err) - } - if handle == nil { - t.Fatalf("missing handle") - } - - go func() { - time.Sleep(100 * time.Millisecond) - err := handle.Signal(syscall.SIGUSR1) - if err != nil { - t.Fatalf("err: %v", err) - } - }() - - // Task should terminate quickly - select { - case res := <-handle.WaitCh(): - if res.Successful() { - t.Fatal("should err") - } - case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second): - t.Fatalf("timeout") - } - - // Check the log file to see it exited because of the signal - outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0") - act, err := ioutil.ReadFile(outputFile) - if err != nil { - t.Fatalf("Couldn't read expected output: %v", err) - } - - exp := "Terminated." - if strings.TrimSpace(string(act)) != exp { - t.Logf("Read from %v", outputFile) - t.Fatalf("Command outputted %v; want %v", act, exp) - } -} diff --git a/client/driver/raw_exec_unix_test.go b/client/driver/raw_exec_unix_test.go new file mode 100644 index 000000000..fd4b3083a --- /dev/null +++ b/client/driver/raw_exec_unix_test.go @@ -0,0 +1,93 @@ +// +build !windows + +package driver + +import ( + "io/ioutil" + "path/filepath" + "strings" + "syscall" + "testing" + "time" + + "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/testutil" +) + +func TestRawExecDriver_Signal(t *testing.T) { + task := &structs.Task{ + Name: "signal", + Driver: "raw_exec", + Config: map[string]interface{}{ + "command": "/bin/bash", + "args": []string{"test.sh"}, + }, + LogConfig: &structs.LogConfig{ + MaxFiles: 10, + MaxFileSizeMB: 10, + }, + Resources: basicResources, + KillTimeout: 10 * time.Second, + } + + ctx := testDriverContexts(t, task) + defer ctx.AllocDir.Destroy() + d := NewRawExecDriver(ctx.DriverCtx) + + testFile := filepath.Join(ctx.ExecCtx.TaskDir.Dir, "test.sh") + testData := []byte(` +at_term() { + echo 'Terminated.' + exit 3 +} +trap at_term USR1 +while true; do + sleep 1 +done + `) + if err := ioutil.WriteFile(testFile, testData, 0777); err != nil { + t.Fatalf("Failed to write data: %v", err) + } + + if _, err := d.Prestart(ctx.ExecCtx, task); err != nil { + t.Fatalf("prestart err: %v", err) + } + handle, err := d.Start(ctx.ExecCtx, task) + if err != nil { + t.Fatalf("err: %v", err) + } + if handle == nil { + t.Fatalf("missing handle") + } + + go func() { + time.Sleep(100 * time.Millisecond) + err := handle.Signal(syscall.SIGUSR1) + if err != nil { + t.Fatalf("err: %v", err) + } + }() + + // Task should terminate quickly + select { + case res := <-handle.WaitCh(): + if res.Successful() { + t.Fatal("should err") + } + case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second): + t.Fatalf("timeout") + } + + // Check the log file to see it exited because of the signal + outputFile := filepath.Join(ctx.ExecCtx.TaskDir.LogDir, "signal.stdout.0") + act, err := ioutil.ReadFile(outputFile) + if err != nil { + t.Fatalf("Couldn't read expected output: %v", err) + } + + exp := "Terminated." + if strings.TrimSpace(string(act)) != exp { + t.Logf("Read from %v", outputFile) + t.Fatalf("Command outputted %v; want %v", act, exp) + } +} diff --git a/client/task_runner_test.go b/client/task_runner_test.go index 6503e88ba..6baa14f97 100644 --- a/client/task_runner_test.go +++ b/client/task_runner_test.go @@ -645,66 +645,6 @@ func TestTaskRunner_RestartTask(t *testing.T) { } } -// This test is just to make sure we are resilient to failures when a restart or -// signal is triggered and the task is not running. -func TestTaskRunner_RestartSignalTask_NotRunning(t *testing.T) { - alloc := mock.Alloc() - task := alloc.Job.TaskGroups[0].Tasks[0] - task.Driver = "mock_driver" - task.Config = map[string]interface{}{ - "exit_code": "0", - "run_for": "100s", - } - - // Use vault to block the start - task.Vault = &structs.Vault{Policies: []string{"default"}} - - ctx := testTaskRunnerFromAlloc(t, true, alloc) - ctx.tr.MarkReceived() - defer ctx.Cleanup() - - // Control when we get a Vault token - token := "1234" - waitCh := make(chan struct{}) - defer close(waitCh) - handler := func(*structs.Allocation, []string) (map[string]string, error) { - <-waitCh - return map[string]string{task.Name: token}, nil - } - ctx.tr.vaultClient.(*vaultclient.MockVaultClient).DeriveTokenFn = handler - go ctx.tr.Run() - - select { - case <-ctx.tr.WaitCh(): - t.Fatalf("premature exit") - case <-time.After(1 * time.Second): - } - - // Send a signal and restart - if err := ctx.tr.Signal("test", "don't panic", syscall.SIGCHLD); err != nil { - t.Fatalf("Signalling errored: %v", err) - } - - // Send a restart - ctx.tr.Restart("test", "don't panic") - - if len(ctx.upd.events) != 2 { - t.Fatalf("should have 2 ctx.updates: %#v", ctx.upd.events) - } - - if ctx.upd.state != structs.TaskStatePending { - t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStatePending) - } - - if ctx.upd.events[0].Type != structs.TaskReceived { - t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) - } - - if ctx.upd.events[1].Type != structs.TaskSetup { - t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) - } -} - func TestTaskRunner_KillTask(t *testing.T) { alloc := mock.Alloc() task := alloc.Job.TaskGroups[0].Tasks[0] diff --git a/client/task_runner_unix_test.go b/client/task_runner_unix_test.go new file mode 100644 index 000000000..bed7c956d --- /dev/null +++ b/client/task_runner_unix_test.go @@ -0,0 +1,73 @@ +// +build !windows + +package client + +import ( + "syscall" + "testing" + "time" + + "github.com/hashicorp/nomad/client/vaultclient" + "github.com/hashicorp/nomad/nomad/mock" + "github.com/hashicorp/nomad/nomad/structs" +) + +// This test is just to make sure we are resilient to failures when a restart or +// signal is triggered and the task is not running. +func TestTaskRunner_RestartSignalTask_NotRunning(t *testing.T) { + alloc := mock.Alloc() + task := alloc.Job.TaskGroups[0].Tasks[0] + task.Driver = "mock_driver" + task.Config = map[string]interface{}{ + "exit_code": "0", + "run_for": "100s", + } + + // Use vault to block the start + task.Vault = &structs.Vault{Policies: []string{"default"}} + + ctx := testTaskRunnerFromAlloc(t, true, alloc) + ctx.tr.MarkReceived() + defer ctx.Cleanup() + + // Control when we get a Vault token + token := "1234" + waitCh := make(chan struct{}) + defer close(waitCh) + handler := func(*structs.Allocation, []string) (map[string]string, error) { + <-waitCh + return map[string]string{task.Name: token}, nil + } + ctx.tr.vaultClient.(*vaultclient.MockVaultClient).DeriveTokenFn = handler + go ctx.tr.Run() + + select { + case <-ctx.tr.WaitCh(): + t.Fatalf("premature exit") + case <-time.After(1 * time.Second): + } + + // Send a signal and restart + if err := ctx.tr.Signal("test", "don't panic", syscall.SIGCHLD); err != nil { + t.Fatalf("Signalling errored: %v", err) + } + + // Send a restart + ctx.tr.Restart("test", "don't panic") + + if len(ctx.upd.events) != 2 { + t.Fatalf("should have 2 ctx.updates: %#v", ctx.upd.events) + } + + if ctx.upd.state != structs.TaskStatePending { + t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStatePending) + } + + if ctx.upd.events[0].Type != structs.TaskReceived { + t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) + } + + if ctx.upd.events[1].Type != structs.TaskSetup { + t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) + } +} diff --git a/testutil/server.go b/testutil/server.go index b64445c9c..bf2a0017d 100644 --- a/testutil/server.go +++ b/testutil/server.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/nomad/helper/discover" ) // offset is used to atomically increment the port numbers. @@ -126,8 +127,9 @@ type TestServer struct { // NewTestServer creates a new TestServer, and makes a call to // an optional callback function to modify the configuration. func NewTestServer(t *testing.T, cb ServerConfigCallback) *TestServer { - if path, err := exec.LookPath("nomad"); err != nil || path == "" { - t.Skip("nomad not found on $PATH, skipping") + path, err := discover.NomadExecutable() + if err != nil { + t.Skipf("nomad not found, skipping: %v", err) } dataDir, err := ioutil.TempDir("", "nomad") @@ -175,7 +177,7 @@ func NewTestServer(t *testing.T, cb ServerConfigCallback) *TestServer { } // Start the server - cmd := exec.Command("nomad", args...) + cmd := exec.Command(path, args...) cmd.Stdout = stdout cmd.Stderr = stderr if err := cmd.Start(); err != nil { diff --git a/testutil/vault.go b/testutil/vault.go index 4ae2bcee1..f15779037 100644 --- a/testutil/vault.go +++ b/testutil/vault.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "os/exec" + "runtime" "testing" "github.com/hashicorp/nomad/nomad/structs" @@ -46,7 +47,11 @@ func NewTestVault(t *testing.T) *TestVault { http := fmt.Sprintf("http://127.0.0.1:%d", port) root := fmt.Sprintf("-dev-root-token-id=%s", token) - cmd := exec.Command("vault", "server", "-dev", bind, root) + bin := "vault" + if runtime.GOOS == "windows" { + bin = "vault.exe" + } + cmd := exec.Command(bin, "server", "-dev", bind, root) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/vendor/github.com/hashicorp/vault/main.go b/vendor/github.com/hashicorp/vault/main.go deleted file mode 100644 index 6cd34fe36..000000000 --- a/vendor/github.com/hashicorp/vault/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main // import "github.com/hashicorp/vault" - -import ( - "os" - - "github.com/hashicorp/vault/cli" -) - -func main() { - os.Exit(cli.Run(os.Args[1:])) -}