github.com/zhizhiboom/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/client/driver/executor/executor_test.go (about) 1 package executor 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path/filepath" 7 "strings" 8 "syscall" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/nomad/client/allocdir" 13 "github.com/hashicorp/nomad/client/driver/env" 14 cstructs "github.com/hashicorp/nomad/client/structs" 15 "github.com/hashicorp/nomad/helper/testlog" 16 "github.com/hashicorp/nomad/nomad/mock" 17 tu "github.com/hashicorp/nomad/testutil" 18 "github.com/mitchellh/go-ps" 19 ) 20 21 // testExecutorContext returns an ExecutorContext and AllocDir. 22 // 23 // The caller is responsible for calling AllocDir.Destroy() to cleanup. 24 func testExecutorContext(t *testing.T) (*ExecutorContext, *allocdir.AllocDir) { 25 alloc := mock.Alloc() 26 task := alloc.Job.TaskGroups[0].Tasks[0] 27 taskEnv := env.NewBuilder(mock.Node(), alloc, task, "global").Build() 28 29 allocDir := allocdir.NewAllocDir(testlog.Logger(t), filepath.Join(os.TempDir(), alloc.ID)) 30 if err := allocDir.Build(); err != nil { 31 t.Fatalf("AllocDir.Build() failed: %v", err) 32 } 33 if err := allocDir.NewTaskDir(task.Name).Build(false, nil, cstructs.FSIsolationNone); err != nil { 34 allocDir.Destroy() 35 t.Fatalf("allocDir.NewTaskDir(%q) failed: %v", task.Name, err) 36 } 37 td := allocDir.TaskDirs[task.Name] 38 ctx := &ExecutorContext{ 39 TaskEnv: taskEnv, 40 Task: task, 41 TaskDir: td.Dir, 42 LogDir: td.LogDir, 43 } 44 return ctx, allocDir 45 } 46 47 func TestExecutor_Start_Invalid(t *testing.T) { 48 t.Parallel() 49 invalid := "/bin/foobar" 50 execCmd := ExecCommand{Cmd: invalid, Args: []string{"1"}} 51 ctx, allocDir := testExecutorContext(t) 52 defer allocDir.Destroy() 53 executor := NewExecutor(testlog.Logger(t)) 54 55 if err := executor.SetContext(ctx); err != nil { 56 t.Fatalf("Unexpected error") 57 } 58 59 if _, err := executor.LaunchCmd(&execCmd); err == nil { 60 t.Fatalf("Expected error") 61 } 62 } 63 64 func TestExecutor_Start_Wait_Failure_Code(t *testing.T) { 65 t.Parallel() 66 execCmd := ExecCommand{Cmd: "/bin/date", Args: []string{"fail"}} 67 ctx, allocDir := testExecutorContext(t) 68 defer allocDir.Destroy() 69 executor := NewExecutor(testlog.Logger(t)) 70 71 if err := executor.SetContext(ctx); err != nil { 72 t.Fatalf("Unexpected error") 73 } 74 75 ps, err := executor.LaunchCmd(&execCmd) 76 if err != nil { 77 t.Fatalf("Unexpected error") 78 } 79 80 if ps.Pid == 0 { 81 t.Fatalf("expected process to start and have non zero pid") 82 } 83 ps, _ = executor.Wait() 84 if ps.ExitCode < 1 { 85 t.Fatalf("expected exit code to be non zero, actual: %v", ps.ExitCode) 86 } 87 if err := executor.Exit(); err != nil { 88 t.Fatalf("error: %v", err) 89 } 90 } 91 92 func TestExecutor_Start_Wait(t *testing.T) { 93 t.Parallel() 94 execCmd := ExecCommand{Cmd: "/bin/echo", Args: []string{"hello world"}} 95 ctx, allocDir := testExecutorContext(t) 96 defer allocDir.Destroy() 97 executor := NewExecutor(testlog.Logger(t)) 98 99 if err := executor.SetContext(ctx); err != nil { 100 t.Fatalf("Unexpected error") 101 } 102 103 ps, err := executor.LaunchCmd(&execCmd) 104 if err != nil { 105 t.Fatalf("error in launching command: %v", err) 106 } 107 if ps.Pid == 0 { 108 t.Fatalf("expected process to start and have non zero pid") 109 } 110 ps, err = executor.Wait() 111 if err != nil { 112 t.Fatalf("error in waiting for command: %v", err) 113 } 114 if err := executor.Exit(); err != nil { 115 t.Fatalf("error: %v", err) 116 } 117 118 expected := "hello world" 119 file := filepath.Join(ctx.LogDir, "web.stdout.0") 120 output, err := ioutil.ReadFile(file) 121 if err != nil { 122 t.Fatalf("Couldn't read file %v", file) 123 } 124 125 act := strings.TrimSpace(string(output)) 126 if act != expected { 127 t.Fatalf("Command output incorrectly: want %v; got %v", expected, act) 128 } 129 } 130 131 func TestExecutor_WaitExitSignal(t *testing.T) { 132 t.Parallel() 133 execCmd := ExecCommand{Cmd: "/bin/sleep", Args: []string{"10000"}} 134 ctx, allocDir := testExecutorContext(t) 135 defer allocDir.Destroy() 136 executor := NewExecutor(testlog.Logger(t)) 137 138 if err := executor.SetContext(ctx); err != nil { 139 t.Fatalf("Unexpected error") 140 } 141 142 ps, err := executor.LaunchCmd(&execCmd) 143 if err != nil { 144 t.Fatalf("err: %v", err) 145 } 146 147 go func() { 148 time.Sleep(2 * time.Second) 149 ru, err := executor.Stats() 150 if err != nil { 151 t.Fatalf("err: %v", err) 152 } 153 if len(ru.Pids) == 0 { 154 t.Fatalf("expected pids") 155 } 156 proc, err := os.FindProcess(ps.Pid) 157 if err != nil { 158 t.Fatalf("err: %v", err) 159 } 160 if err := proc.Signal(syscall.SIGKILL); err != nil { 161 t.Fatalf("err: %v", err) 162 } 163 }() 164 165 ps, err = executor.Wait() 166 if err != nil { 167 t.Fatalf("err: %v", err) 168 } 169 if ps.Signal != int(syscall.SIGKILL) { 170 t.Fatalf("expected signal: %v, actual: %v", int(syscall.SIGKILL), ps.Signal) 171 } 172 } 173 174 func TestExecutor_Start_Kill(t *testing.T) { 175 t.Parallel() 176 execCmd := ExecCommand{Cmd: "/bin/sleep", Args: []string{"10 && hello world"}} 177 ctx, allocDir := testExecutorContext(t) 178 defer allocDir.Destroy() 179 executor := NewExecutor(testlog.Logger(t)) 180 181 if err := executor.SetContext(ctx); err != nil { 182 t.Fatalf("Unexpected error") 183 } 184 185 ps, err := executor.LaunchCmd(&execCmd) 186 if err != nil { 187 t.Fatalf("error in launching command: %v", err) 188 } 189 if ps.Pid == 0 { 190 t.Fatalf("expected process to start and have non zero pid") 191 } 192 ps, err = executor.Wait() 193 if err != nil { 194 t.Fatalf("error in waiting for command: %v", err) 195 } 196 if err := executor.Exit(); err != nil { 197 t.Fatalf("error: %v", err) 198 } 199 200 file := filepath.Join(ctx.LogDir, "web.stdout.0") 201 time.Sleep(time.Duration(tu.TestMultiplier()*2) * time.Second) 202 203 output, err := ioutil.ReadFile(file) 204 if err != nil { 205 t.Fatalf("Couldn't read file %v", file) 206 } 207 208 expected := "" 209 act := strings.TrimSpace(string(output)) 210 if act != expected { 211 t.Fatalf("Command output incorrectly: want %v; got %v", expected, act) 212 } 213 } 214 215 func TestExecutor_MakeExecutable(t *testing.T) { 216 t.Parallel() 217 // Create a temp file 218 f, err := ioutil.TempFile("", "") 219 if err != nil { 220 t.Fatal(err) 221 } 222 defer f.Close() 223 defer os.Remove(f.Name()) 224 225 // Set its permissions to be non-executable 226 f.Chmod(os.FileMode(0610)) 227 228 // Make a fake executor 229 executor := NewExecutor(testlog.Logger(t)) 230 231 err = executor.(*UniversalExecutor).makeExecutable(f.Name()) 232 if err != nil { 233 t.Fatalf("makeExecutable() failed: %v", err) 234 } 235 236 // Check the permissions 237 stat, err := f.Stat() 238 if err != nil { 239 t.Fatalf("Stat() failed: %v", err) 240 } 241 242 act := stat.Mode().Perm() 243 exp := os.FileMode(0755) 244 if act != exp { 245 t.Fatalf("expected permissions %v; got %v", exp, act) 246 } 247 } 248 249 func TestScanPids(t *testing.T) { 250 t.Parallel() 251 p1 := NewFakeProcess(2, 5) 252 p2 := NewFakeProcess(10, 2) 253 p3 := NewFakeProcess(15, 6) 254 p4 := NewFakeProcess(3, 10) 255 p5 := NewFakeProcess(20, 18) 256 257 // Make a fake executor 258 executor := NewExecutor(testlog.Logger(t)).(*UniversalExecutor) 259 260 nomadPids, err := executor.scanPids(5, []ps.Process{p1, p2, p3, p4, p5}) 261 if err != nil { 262 t.Fatalf("error: %v", err) 263 } 264 if len(nomadPids) != 4 { 265 t.Fatalf("expected: 4, actual: %v", len(nomadPids)) 266 } 267 } 268 269 type FakeProcess struct { 270 pid int 271 ppid int 272 } 273 274 func (f FakeProcess) Pid() int { 275 return f.pid 276 } 277 278 func (f FakeProcess) PPid() int { 279 return f.ppid 280 } 281 282 func (f FakeProcess) Executable() string { 283 return "fake" 284 } 285 286 func NewFakeProcess(pid int, ppid int) ps.Process { 287 return FakeProcess{pid: pid, ppid: ppid} 288 }