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