package executor import ( "fmt" "io/ioutil" "os" "path/filepath" "testing" "time" "github.com/hashicorp/nomad/client/allocdir" "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" ctestutil "github.com/hashicorp/nomad/client/testutil" ) var ( constraint = &structs.Resources{ CPU: 250, MemoryMB: 256, Networks: []*structs.NetworkResource{ &structs.NetworkResource{ MBits: 50, DynamicPorts: []string{"http"}, }, }, } ) func mockAllocDir(t *testing.T) (string, *allocdir.AllocDir) { alloc := mock.Alloc() task := alloc.Job.TaskGroups[0].Tasks[0] allocDir := allocdir.NewAllocDir(filepath.Join(os.TempDir(), alloc.ID)) if err := allocDir.Build([]*structs.Task{task}); err != nil { t.Fatalf("allocDir.Build() failed: %v", err) } return task.Name, allocDir } func TestExecutorLinux_Start_Invalid(t *testing.T) { ctestutil.ExecCompatible(t) invalid := "/bin/foobar" e := Command(invalid, "1") if err := e.Limit(constraint); err != nil { t.Fatalf("Limit() failed: %v", err) } task, alloc := mockAllocDir(t) defer alloc.Destroy() if err := e.ConfigureTaskDir(task, alloc); err != nil { t.Fatalf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err) } if err := e.Start(); err == nil { t.Fatalf("Start(%v) should have failed", invalid) } } func TestExecutorLinux_Start_Wait_Failure_Code(t *testing.T) { ctestutil.ExecCompatible(t) e := Command("/bin/date", "-invalid") if err := e.Limit(constraint); err != nil { t.Fatalf("Limit() failed: %v", err) } task, alloc := mockAllocDir(t) defer alloc.Destroy() if err := e.ConfigureTaskDir(task, alloc); err != nil { t.Fatalf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err) } if err := e.Start(); err != nil { t.Fatalf("Start() failed: %v", err) } if err := e.Wait(); err == nil { t.Fatalf("Wait() should have failed") } } func TestExecutorLinux_Start_Wait(t *testing.T) { ctestutil.ExecCompatible(t) task, alloc := mockAllocDir(t) defer alloc.Destroy() taskDir, ok := alloc.TaskDirs[task] if !ok { t.Fatalf("No task directory found for task %v", task) } expected := "hello world" file := filepath.Join(allocdir.TaskLocal, "output.txt") absFilePath := filepath.Join(taskDir, file) cmd := fmt.Sprintf(`"%v \"%v\" > %v"`, "/bin/sleep 1 ; echo -n", expected, file) e := Command("/bin/bash", "-c", cmd) if err := e.Limit(constraint); err != nil { t.Fatalf("Limit() failed: %v", err) } if err := e.ConfigureTaskDir(task, alloc); err != nil { t.Fatalf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err) } if err := e.Start(); err != nil { t.Fatalf("Start() failed: %v", err) } if err := e.Wait(); err != nil { t.Fatalf("Wait() failed: %v", err) } output, err := ioutil.ReadFile(absFilePath) if err != nil { t.Fatalf("Couldn't read file %v", absFilePath) } act := string(output) if act != expected { t.Fatalf("Command output incorrectly: want %v; got %v", expected, act) } } func TestExecutorLinux_Start_Kill(t *testing.T) { ctestutil.ExecCompatible(t) task, alloc := mockAllocDir(t) defer alloc.Destroy() taskDir, ok := alloc.TaskDirs[task] if !ok { t.Fatalf("No task directory found for task %v", task) } filePath := filepath.Join(taskDir, "output") e := Command("/bin/bash", "-c", "sleep 1 ; echo \"failure\" > "+filePath) // This test can only be run if cgroups are enabled. if !e.(*LinuxExecutor).cgroupEnabled { t.SkipNow() } if err := e.Limit(constraint); err != nil { t.Fatalf("Limit() failed: %v", err) } if err := e.ConfigureTaskDir(task, alloc); err != nil { t.Fatalf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err) } if err := e.Start(); err != nil { t.Fatalf("Start() failed: %v", err) } if err := e.Shutdown(); err != nil { t.Fatalf("Shutdown() failed: %v", err) } time.Sleep(1500 * time.Millisecond) // Check that the file doesn't exist. if _, err := os.Stat(filePath); err == nil { t.Fatalf("Stat(%v) should have failed: task not killed", filePath) } } func TestExecutorLinux_Open(t *testing.T) { ctestutil.ExecCompatible(t) task, alloc := mockAllocDir(t) defer alloc.Destroy() taskDir, ok := alloc.TaskDirs[task] if !ok { t.Fatalf("No task directory found for task %v", task) } filePath := filepath.Join(taskDir, "output") e := Command("/bin/bash", "-c", "sleep 1 ; echo \"failure\" > "+filePath) // This test can only be run if cgroups are enabled. if !e.(*LinuxExecutor).cgroupEnabled { t.SkipNow() } if err := e.Limit(constraint); err != nil { t.Fatalf("Limit() failed: %v", err) } if err := e.ConfigureTaskDir(task, alloc); err != nil { t.Fatalf("ConfigureTaskDir(%v, %v) failed: %v", task, alloc, err) } if err := e.Start(); err != nil { t.Fatalf("Start() failed: %v", err) } id, err := e.ID() if err != nil { t.Fatalf("ID() failed: %v", err) } if _, err := OpenId(id); err == nil { t.Fatalf("Open(%v) should have failed", id) } time.Sleep(1500 * time.Millisecond) // Check that the file doesn't exist, open should have killed the process. if _, err := os.Stat(filePath); err == nil { t.Fatalf("Stat(%v) should have failed: task not killed", filePath) } }