github.com/huiliang/nomad@v0.2.1-0.20151124023127-7a8b664699ff/client/driver/spawn/spawn_test.go (about) 1 package spawn 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "strings" 9 "testing" 10 "time" 11 ) 12 13 func TestMain(m *testing.M) { 14 switch os.Getenv("TEST_MAIN") { 15 case "app": 16 appMain() 17 default: 18 os.Exit(m.Run()) 19 } 20 } 21 22 func appMain() { 23 if len(os.Args) < 2 { 24 fmt.Fprintln(os.Stderr, "no command provided") 25 os.Exit(1) 26 } 27 switch cmd := os.Args[1]; cmd { 28 case "echo": 29 fmt.Println(strings.Join(os.Args[2:], " ")) 30 case "sleep": 31 if len(os.Args) != 3 { 32 fmt.Fprintln(os.Stderr, "expected 3 args") 33 os.Exit(1) 34 } 35 dur, err := time.ParseDuration(os.Args[2]) 36 if err != nil { 37 fmt.Fprintf(os.Stderr, "could not parse sleep time: %v", err) 38 os.Exit(1) 39 } 40 time.Sleep(dur) 41 default: 42 fmt.Fprintln(os.Stderr, "unknown command:", cmd) 43 os.Exit(1) 44 } 45 } 46 47 func TestSpawn_NoCmd(t *testing.T) { 48 t.Parallel() 49 tempFile := tempFileName(t) 50 defer os.Remove(tempFile) 51 52 spawn := NewSpawner(tempFile) 53 if err := spawn.Spawn(nil); err == nil { 54 t.Fatalf("Spawn() with no user command should fail") 55 } 56 } 57 58 func TestSpawn_InvalidCmd(t *testing.T) { 59 t.Parallel() 60 tempFile := tempFileName(t) 61 defer os.Remove(tempFile) 62 63 spawn := NewSpawner(tempFile) 64 spawn.SetCommand(exec.Command("foo")) // non-existent command 65 if err := spawn.Spawn(nil); err == nil { 66 t.Fatalf("Spawn() with an invalid command should fail") 67 } 68 } 69 70 func TestSpawn_SetsLogs(t *testing.T) { 71 t.Parallel() 72 tempFile := tempFileName(t) 73 defer os.Remove(tempFile) 74 75 spawn := NewSpawner(tempFile) 76 exp := "foo" 77 spawn.SetCommand(testCommand("echo", exp)) 78 79 // Create file for stdout. 80 stdout := tempFileName(t) 81 defer os.Remove(stdout) 82 spawn.SetLogs(&Logs{Stdout: stdout}) 83 84 if err := spawn.Spawn(nil); err != nil { 85 t.Fatalf("Spawn() failed: %v", err) 86 } 87 88 if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil { 89 t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err) 90 } 91 92 stdout2, err := os.Open(stdout) 93 if err != nil { 94 t.Fatalf("Open() failed: %v", err) 95 } 96 97 data, err := ioutil.ReadAll(stdout2) 98 if err != nil { 99 t.Fatalf("ReadAll() failed: %v", err) 100 } 101 102 act := strings.TrimSpace(string(data)) 103 if act != exp { 104 t.Fatalf("Unexpected data written to stdout; got %v; want %v", act, exp) 105 } 106 } 107 108 func TestSpawn_Callback(t *testing.T) { 109 t.Parallel() 110 tempFile := tempFileName(t) 111 defer os.Remove(tempFile) 112 113 spawn := NewSpawner(tempFile) 114 spawn.SetCommand(testCommand("sleep", "1s")) 115 116 called := false 117 cbErr := fmt.Errorf("ERROR CB") 118 cb := func(_ int) error { 119 called = true 120 return cbErr 121 } 122 123 if err := spawn.Spawn(cb); err == nil { 124 t.Fatalf("Spawn(%#v) should have errored; want %v", cb, cbErr) 125 } 126 127 if !called { 128 t.Fatalf("Spawn(%#v) didn't call callback", cb) 129 } 130 } 131 132 func TestSpawn_ParentWaitExited(t *testing.T) { 133 t.Parallel() 134 tempFile := tempFileName(t) 135 defer os.Remove(tempFile) 136 137 spawn := NewSpawner(tempFile) 138 spawn.SetCommand(testCommand("echo", "foo")) 139 if err := spawn.Spawn(nil); err != nil { 140 t.Fatalf("Spawn() failed %v", err) 141 } 142 143 time.Sleep(1 * time.Second) 144 145 if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil { 146 t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err) 147 } 148 } 149 150 func TestSpawn_ParentWait(t *testing.T) { 151 t.Parallel() 152 tempFile := tempFileName(t) 153 defer os.Remove(tempFile) 154 155 spawn := NewSpawner(tempFile) 156 spawn.SetCommand(testCommand("sleep", "2s")) 157 if err := spawn.Spawn(nil); err != nil { 158 t.Fatalf("Spawn() failed %v", err) 159 } 160 161 if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil { 162 t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err) 163 } 164 } 165 166 func TestSpawn_NonParentWaitExited(t *testing.T) { 167 t.Parallel() 168 tempFile := tempFileName(t) 169 defer os.Remove(tempFile) 170 171 spawn := NewSpawner(tempFile) 172 spawn.SetCommand(testCommand("echo", "foo")) 173 if err := spawn.Spawn(nil); err != nil { 174 t.Fatalf("Spawn() failed %v", err) 175 } 176 177 time.Sleep(1 * time.Second) 178 179 // Force the wait to assume non-parent. 180 spawn.SpawnPpid = 0 181 if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil { 182 t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err) 183 } 184 } 185 186 func TestSpawn_NonParentWait(t *testing.T) { 187 t.Parallel() 188 tempFile := tempFileName(t) 189 defer os.Remove(tempFile) 190 191 spawn := NewSpawner(tempFile) 192 spawn.SetCommand(testCommand("sleep", "2s")) 193 if err := spawn.Spawn(nil); err != nil { 194 t.Fatalf("Spawn() failed %v", err) 195 } 196 197 // Need to wait on the spawner, otherwise it becomes a zombie and the test 198 // only finishes after the init process cleans it. This speeds that up. 199 go func() { 200 time.Sleep(3 * time.Second) 201 if _, err := spawn.spawn.Wait(); err != nil { 202 t.FailNow() 203 } 204 }() 205 206 // Force the wait to assume non-parent. 207 spawn.SpawnPpid = 0 208 if res := spawn.Wait(); res.ExitCode != 0 && res.Err != nil { 209 t.Fatalf("Wait() returned %v, %v; want 0, nil", res.ExitCode, res.Err) 210 } 211 } 212 213 func TestSpawn_DeadSpawnDaemon_Parent(t *testing.T) { 214 t.Parallel() 215 tempFile := tempFileName(t) 216 defer os.Remove(tempFile) 217 218 var spawnPid int 219 cb := func(pid int) error { 220 spawnPid = pid 221 return nil 222 } 223 224 spawn := NewSpawner(tempFile) 225 spawn.SetCommand(testCommand("sleep", "5s")) 226 if err := spawn.Spawn(cb); err != nil { 227 t.Fatalf("Spawn() errored: %v", err) 228 } 229 230 proc, err := os.FindProcess(spawnPid) 231 if err != nil { 232 t.FailNow() 233 } 234 235 if err := proc.Kill(); err != nil { 236 t.FailNow() 237 } 238 239 if _, err := proc.Wait(); err != nil { 240 t.FailNow() 241 } 242 243 if res := spawn.Wait(); res.Err == nil { 244 t.Fatalf("Wait() should have failed: %v", res.Err) 245 } 246 } 247 248 func TestSpawn_DeadSpawnDaemon_NonParent(t *testing.T) { 249 t.Parallel() 250 tempFile := tempFileName(t) 251 defer os.Remove(tempFile) 252 253 var spawnPid int 254 cb := func(pid int) error { 255 spawnPid = pid 256 return nil 257 } 258 259 spawn := NewSpawner(tempFile) 260 spawn.SetCommand(testCommand("sleep", "2s")) 261 if err := spawn.Spawn(cb); err != nil { 262 t.Fatalf("Spawn() errored: %v", err) 263 } 264 265 proc, err := os.FindProcess(spawnPid) 266 if err != nil { 267 t.FailNow() 268 } 269 270 if err := proc.Kill(); err != nil { 271 t.FailNow() 272 } 273 274 if _, err := proc.Wait(); err != nil { 275 t.FailNow() 276 } 277 278 // Force the wait to assume non-parent. 279 spawn.SpawnPpid = 0 280 if res := spawn.Wait(); res.Err == nil { 281 t.Fatalf("Wait() should have failed: %v", res.Err) 282 } 283 } 284 285 func TestSpawn_Valid_TaskRunning(t *testing.T) { 286 t.Parallel() 287 tempFile := tempFileName(t) 288 defer os.Remove(tempFile) 289 290 spawn := NewSpawner(tempFile) 291 spawn.SetCommand(testCommand("sleep", "2s")) 292 if err := spawn.Spawn(nil); err != nil { 293 t.Fatalf("Spawn() failed %v", err) 294 } 295 296 if err := spawn.Valid(); err != nil { 297 t.Fatalf("Valid() failed: %v", err) 298 } 299 300 if res := spawn.Wait(); res.Err != nil { 301 t.Fatalf("Wait() failed: %v", res.Err) 302 } 303 } 304 305 func TestSpawn_Valid_TaskExit_ExitCode(t *testing.T) { 306 t.Parallel() 307 tempFile := tempFileName(t) 308 defer os.Remove(tempFile) 309 310 spawn := NewSpawner(tempFile) 311 spawn.SetCommand(testCommand("echo", "foo")) 312 if err := spawn.Spawn(nil); err != nil { 313 t.Fatalf("Spawn() failed %v", err) 314 } 315 316 if res := spawn.Wait(); res.Err != nil { 317 t.Fatalf("Wait() failed: %v", res.Err) 318 } 319 320 if err := spawn.Valid(); err != nil { 321 t.Fatalf("Valid() failed: %v", err) 322 } 323 } 324 325 func TestSpawn_Valid_TaskExit_NoExitCode(t *testing.T) { 326 t.Parallel() 327 tempFile := tempFileName(t) 328 defer os.Remove(tempFile) 329 330 spawn := NewSpawner(tempFile) 331 spawn.SetCommand(testCommand("echo", "foo")) 332 if err := spawn.Spawn(nil); err != nil { 333 t.Fatalf("Spawn() failed %v", err) 334 } 335 336 if res := spawn.Wait(); res.Err != nil { 337 t.Fatalf("Wait() failed: %v", res.Err) 338 } 339 340 // Delete the file so that it can't find the exit code. 341 os.Remove(tempFile) 342 343 if err := spawn.Valid(); err == nil { 344 t.Fatalf("Valid() should have failed") 345 } 346 } 347 348 func tempFileName(t *testing.T) string { 349 f, err := ioutil.TempFile("", "") 350 if err != nil { 351 t.Fatalf("TempFile() failed") 352 } 353 defer f.Close() 354 return f.Name() 355 } 356 357 func testCommand(args ...string) *exec.Cmd { 358 cmd := exec.Command(os.Args[0], args...) 359 cmd.Env = append(os.Environ(), "TEST_MAIN=app") 360 return cmd 361 }