github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/task_runner_test.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "net/http" 8 "net/http/httptest" 9 "os" 10 "path/filepath" 11 "reflect" 12 "syscall" 13 "testing" 14 "time" 15 16 "github.com/golang/snappy" 17 "github.com/hashicorp/nomad/client/allocdir" 18 "github.com/hashicorp/nomad/client/config" 19 cstructs "github.com/hashicorp/nomad/client/structs" 20 "github.com/hashicorp/nomad/client/vaultclient" 21 "github.com/hashicorp/nomad/nomad/mock" 22 "github.com/hashicorp/nomad/nomad/structs" 23 "github.com/hashicorp/nomad/testutil" 24 25 ctestutil "github.com/hashicorp/nomad/client/testutil" 26 ) 27 28 func testLogger() *log.Logger { 29 return prefixedTestLogger("") 30 } 31 32 func prefixedTestLogger(prefix string) *log.Logger { 33 return log.New(os.Stderr, prefix, log.LstdFlags) 34 } 35 36 type MockTaskStateUpdater struct { 37 state string 38 failed bool 39 events []*structs.TaskEvent 40 } 41 42 func (m *MockTaskStateUpdater) Update(name, state string, event *structs.TaskEvent) { 43 if state != "" { 44 m.state = state 45 } 46 if event != nil { 47 if event.FailsTask { 48 m.failed = true 49 } 50 m.events = append(m.events, event) 51 } 52 } 53 54 type taskRunnerTestCtx struct { 55 upd *MockTaskStateUpdater 56 tr *TaskRunner 57 allocDir *allocdir.AllocDir 58 } 59 60 // Cleanup calls Destroy on the task runner and alloc dir 61 func (ctx *taskRunnerTestCtx) Cleanup() { 62 ctx.tr.Destroy(structs.NewTaskEvent(structs.TaskKilled)) 63 ctx.allocDir.Destroy() 64 } 65 66 func testTaskRunner(t *testing.T, restarts bool) *taskRunnerTestCtx { 67 return testTaskRunnerFromAlloc(t, restarts, mock.Alloc()) 68 } 69 70 // Creates a mock task runner using the first task in the first task group of 71 // the passed allocation. 72 // 73 // Callers should defer Cleanup() to cleanup after completion 74 func testTaskRunnerFromAlloc(t *testing.T, restarts bool, alloc *structs.Allocation) *taskRunnerTestCtx { 75 logger := testLogger() 76 conf := config.DefaultConfig() 77 conf.StateDir = os.TempDir() 78 conf.AllocDir = os.TempDir() 79 upd := &MockTaskStateUpdater{} 80 task := alloc.Job.TaskGroups[0].Tasks[0] 81 // Initialize the port listing. This should be done by the offer process but 82 // we have a mock so that doesn't happen. 83 task.Resources.Networks[0].ReservedPorts = []structs.Port{{Label: "", Value: 80}} 84 85 allocDir := allocdir.NewAllocDir(testLogger(), filepath.Join(conf.AllocDir, alloc.ID)) 86 if err := allocDir.Build(); err != nil { 87 t.Fatalf("error building alloc dir: %v", err) 88 return nil 89 } 90 91 //HACK to get FSIsolation and chroot without using AllocRunner, 92 // TaskRunner, or Drivers 93 fsi := cstructs.FSIsolationImage 94 switch task.Driver { 95 case "raw_exec": 96 fsi = cstructs.FSIsolationNone 97 case "exec", "java": 98 fsi = cstructs.FSIsolationChroot 99 } 100 taskDir := allocDir.NewTaskDir(task.Name) 101 if err := taskDir.Build(false, config.DefaultChrootEnv, fsi); err != nil { 102 t.Fatalf("error building task dir %q: %v", task.Name, err) 103 return nil 104 } 105 106 vclient := vaultclient.NewMockVaultClient() 107 tr := NewTaskRunner(logger, conf, upd.Update, taskDir, alloc, task, vclient) 108 if !restarts { 109 tr.restartTracker = noRestartsTracker() 110 } 111 return &taskRunnerTestCtx{upd, tr, allocDir} 112 } 113 114 // testWaitForTaskToStart waits for the task to or fails the test 115 func testWaitForTaskToStart(t *testing.T, ctx *taskRunnerTestCtx) { 116 // Wait for the task to start 117 testutil.WaitForResult(func() (bool, error) { 118 l := len(ctx.upd.events) 119 if l < 2 { 120 return false, fmt.Errorf("Expect two events; got %v", l) 121 } 122 123 if ctx.upd.events[0].Type != structs.TaskReceived { 124 return false, fmt.Errorf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 125 } 126 127 if l >= 3 { 128 if ctx.upd.events[1].Type != structs.TaskSetup { 129 return false, fmt.Errorf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 130 } 131 if ctx.upd.events[2].Type != structs.TaskStarted { 132 return false, fmt.Errorf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 133 } 134 } else { 135 if ctx.upd.events[1].Type != structs.TaskStarted { 136 return false, fmt.Errorf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskStarted) 137 } 138 } 139 140 return true, nil 141 }, func(err error) { 142 t.Fatalf("err: %v", err) 143 }) 144 } 145 146 func TestTaskRunner_SimpleRun(t *testing.T) { 147 ctestutil.ExecCompatible(t) 148 ctx := testTaskRunner(t, false) 149 ctx.tr.MarkReceived() 150 go ctx.tr.Run() 151 defer ctx.Cleanup() 152 153 select { 154 case <-ctx.tr.WaitCh(): 155 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 156 t.Fatalf("timeout") 157 } 158 159 if len(ctx.upd.events) != 4 { 160 t.Fatalf("should have 3 ctx.updates: %#v", ctx.upd.events) 161 } 162 163 if ctx.upd.state != structs.TaskStateDead { 164 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 165 } 166 167 if ctx.upd.events[0].Type != structs.TaskReceived { 168 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 169 } 170 171 if ctx.upd.events[1].Type != structs.TaskSetup { 172 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 173 } 174 175 if ctx.upd.events[2].Type != structs.TaskStarted { 176 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 177 } 178 179 if ctx.upd.events[3].Type != structs.TaskTerminated { 180 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskTerminated) 181 } 182 } 183 184 func TestTaskRunner_Run_RecoverableStartError(t *testing.T) { 185 alloc := mock.Alloc() 186 task := alloc.Job.TaskGroups[0].Tasks[0] 187 task.Driver = "mock_driver" 188 task.Config = map[string]interface{}{ 189 "exit_code": 0, 190 "start_error": "driver failure", 191 "start_error_recoverable": true, 192 } 193 194 ctx := testTaskRunnerFromAlloc(t, true, alloc) 195 ctx.tr.MarkReceived() 196 go ctx.tr.Run() 197 defer ctx.Cleanup() 198 199 testutil.WaitForResult(func() (bool, error) { 200 if l := len(ctx.upd.events); l < 4 { 201 return false, fmt.Errorf("Expect at least four events; got %v", l) 202 } 203 204 if ctx.upd.events[0].Type != structs.TaskReceived { 205 return false, fmt.Errorf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 206 } 207 208 if ctx.upd.events[1].Type != structs.TaskSetup { 209 return false, fmt.Errorf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 210 } 211 212 if ctx.upd.events[2].Type != structs.TaskDriverFailure { 213 return false, fmt.Errorf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskDriverFailure) 214 } 215 216 if ctx.upd.events[3].Type != structs.TaskRestarting { 217 return false, fmt.Errorf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskRestarting) 218 } 219 220 return true, nil 221 }, func(err error) { 222 t.Fatalf("err: %v", err) 223 }) 224 } 225 226 func TestTaskRunner_Destroy(t *testing.T) { 227 ctestutil.ExecCompatible(t) 228 ctx := testTaskRunner(t, true) 229 ctx.tr.MarkReceived() 230 //FIXME This didn't used to send a kill status update!!!??? 231 defer ctx.Cleanup() 232 233 // Change command to ensure we run for a bit 234 ctx.tr.task.Config["command"] = "/bin/sleep" 235 ctx.tr.task.Config["args"] = []string{"1000"} 236 go ctx.tr.Run() 237 238 // Wait for the task to start 239 testWaitForTaskToStart(t, ctx) 240 241 // Make sure we are collecting a few stats 242 time.Sleep(2 * time.Second) 243 stats := ctx.tr.LatestResourceUsage() 244 if len(stats.Pids) == 0 || stats.ResourceUsage == nil || stats.ResourceUsage.MemoryStats.RSS == 0 { 245 t.Fatalf("expected task runner to have some stats") 246 } 247 248 // Begin the tear down 249 ctx.tr.Destroy(structs.NewTaskEvent(structs.TaskKilled)) 250 251 select { 252 case <-ctx.tr.WaitCh(): 253 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 254 t.Fatalf("timeout") 255 } 256 257 if len(ctx.upd.events) != 5 { 258 t.Fatalf("should have 5 ctx.updates: %#v", ctx.upd.events) 259 } 260 261 if ctx.upd.state != structs.TaskStateDead { 262 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 263 } 264 265 if ctx.upd.events[3].Type != structs.TaskKilling { 266 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskKilling) 267 } 268 269 if ctx.upd.events[4].Type != structs.TaskKilled { 270 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskKilled) 271 } 272 } 273 274 func TestTaskRunner_Update(t *testing.T) { 275 ctestutil.ExecCompatible(t) 276 ctx := testTaskRunner(t, false) 277 278 // Change command to ensure we run for a bit 279 ctx.tr.task.Config["command"] = "/bin/sleep" 280 ctx.tr.task.Config["args"] = []string{"100"} 281 go ctx.tr.Run() 282 defer ctx.Cleanup() 283 284 // Update the task definition 285 updateAlloc := ctx.tr.alloc.Copy() 286 287 // Update the restart policy 288 newTG := updateAlloc.Job.TaskGroups[0] 289 newMode := "foo" 290 newTG.RestartPolicy.Mode = newMode 291 292 newTask := updateAlloc.Job.TaskGroups[0].Tasks[0] 293 newTask.Driver = "foobar" 294 295 // Update the kill timeout 296 testutil.WaitForResult(func() (bool, error) { 297 if ctx.tr.handle == nil { 298 return false, fmt.Errorf("task not started") 299 } 300 return true, nil 301 }, func(err error) { 302 t.Fatalf("err: %v", err) 303 }) 304 305 oldHandle := ctx.tr.handle.ID() 306 newTask.KillTimeout = time.Hour 307 308 ctx.tr.Update(updateAlloc) 309 310 // Wait for ctx.update to take place 311 testutil.WaitForResult(func() (bool, error) { 312 if ctx.tr.task == newTask { 313 return false, fmt.Errorf("We copied the pointer! This would be very bad") 314 } 315 if ctx.tr.task.Driver != newTask.Driver { 316 return false, fmt.Errorf("Task not copied") 317 } 318 if ctx.tr.restartTracker.policy.Mode != newMode { 319 return false, fmt.Errorf("restart policy not ctx.updated") 320 } 321 if ctx.tr.handle.ID() == oldHandle { 322 return false, fmt.Errorf("handle not ctx.updated") 323 } 324 return true, nil 325 }, func(err error) { 326 t.Fatalf("err: %v", err) 327 }) 328 } 329 330 func TestTaskRunner_SaveRestoreState(t *testing.T) { 331 alloc := mock.Alloc() 332 task := alloc.Job.TaskGroups[0].Tasks[0] 333 task.Driver = "mock_driver" 334 task.Config = map[string]interface{}{ 335 "exit_code": "0", 336 "run_for": "5s", 337 } 338 339 // Give it a Vault token 340 task.Vault = &structs.Vault{Policies: []string{"default"}} 341 342 ctx := testTaskRunnerFromAlloc(t, false, alloc) 343 ctx.tr.MarkReceived() 344 go ctx.tr.Run() 345 //FIXME This test didn't used to defer destroy the allocidr ???!!! 346 defer ctx.Cleanup() 347 348 // Wait for the task to be running and then snapshot the state 349 testWaitForTaskToStart(t, ctx) 350 351 if err := ctx.tr.SaveState(); err != nil { 352 t.Fatalf("err: %v", err) 353 } 354 355 // Read the token from the file system 356 tokenPath := filepath.Join(ctx.tr.taskDir.SecretsDir, vaultTokenFile) 357 data, err := ioutil.ReadFile(tokenPath) 358 if err != nil { 359 t.Fatalf("Failed to read file: %v", err) 360 } 361 token := string(data) 362 if len(token) == 0 { 363 t.Fatalf("Token not written to disk") 364 } 365 366 // Create a new task runner 367 task2 := &structs.Task{Name: ctx.tr.task.Name, Driver: ctx.tr.task.Driver} 368 tr2 := NewTaskRunner(ctx.tr.logger, ctx.tr.config, ctx.upd.Update, 369 ctx.tr.taskDir, ctx.tr.alloc, task2, ctx.tr.vaultClient) 370 tr2.restartTracker = noRestartsTracker() 371 if err := tr2.RestoreState(); err != nil { 372 t.Fatalf("err: %v", err) 373 } 374 go tr2.Run() 375 defer tr2.Destroy(structs.NewTaskEvent(structs.TaskKilled)) 376 377 // Destroy and wait 378 select { 379 case <-tr2.WaitCh(): 380 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 381 t.Fatalf("timeout") 382 } 383 384 // Check that we recovered the token 385 if act := tr2.vaultFuture.Get(); act != token { 386 t.Fatalf("Vault token not properly recovered") 387 } 388 } 389 390 func TestTaskRunner_Download_List(t *testing.T) { 391 ctestutil.ExecCompatible(t) 392 393 ts := httptest.NewServer(http.FileServer(http.Dir(filepath.Dir(".")))) 394 defer ts.Close() 395 396 // Create an allocation that has a task with a list of artifacts. 397 alloc := mock.Alloc() 398 task := alloc.Job.TaskGroups[0].Tasks[0] 399 f1 := "task_runner_test.go" 400 f2 := "task_runner.go" 401 artifact1 := structs.TaskArtifact{ 402 GetterSource: fmt.Sprintf("%s/%s", ts.URL, f1), 403 } 404 artifact2 := structs.TaskArtifact{ 405 GetterSource: fmt.Sprintf("%s/%s", ts.URL, f2), 406 } 407 task.Artifacts = []*structs.TaskArtifact{&artifact1, &artifact2} 408 409 ctx := testTaskRunnerFromAlloc(t, false, alloc) 410 ctx.tr.MarkReceived() 411 go ctx.tr.Run() 412 defer ctx.Cleanup() 413 414 select { 415 case <-ctx.tr.WaitCh(): 416 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 417 t.Fatalf("timeout") 418 } 419 420 if len(ctx.upd.events) != 5 { 421 t.Fatalf("should have 5 ctx.updates: %#v", ctx.upd.events) 422 } 423 424 if ctx.upd.state != structs.TaskStateDead { 425 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 426 } 427 428 if ctx.upd.events[0].Type != structs.TaskReceived { 429 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 430 } 431 432 if ctx.upd.events[1].Type != structs.TaskSetup { 433 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 434 } 435 436 if ctx.upd.events[2].Type != structs.TaskDownloadingArtifacts { 437 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskDownloadingArtifacts) 438 } 439 440 if ctx.upd.events[3].Type != structs.TaskStarted { 441 t.Fatalf("Forth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskStarted) 442 } 443 444 if ctx.upd.events[4].Type != structs.TaskTerminated { 445 t.Fatalf("Fifth Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskTerminated) 446 } 447 448 // Check that both files exist. 449 if _, err := os.Stat(filepath.Join(ctx.tr.taskDir.Dir, f1)); err != nil { 450 t.Fatalf("%v not downloaded", f1) 451 } 452 if _, err := os.Stat(filepath.Join(ctx.tr.taskDir.Dir, f2)); err != nil { 453 t.Fatalf("%v not downloaded", f2) 454 } 455 } 456 457 func TestTaskRunner_Download_Retries(t *testing.T) { 458 ctestutil.ExecCompatible(t) 459 460 // Create an allocation that has a task with bad artifacts. 461 alloc := mock.Alloc() 462 task := alloc.Job.TaskGroups[0].Tasks[0] 463 artifact := structs.TaskArtifact{ 464 GetterSource: "http://127.1.1.111:12315/foo/bar/baz", 465 } 466 task.Artifacts = []*structs.TaskArtifact{&artifact} 467 468 // Make the restart policy try one ctx.upd.te 469 alloc.Job.TaskGroups[0].RestartPolicy = &structs.RestartPolicy{ 470 Attempts: 1, 471 Interval: 10 * time.Minute, 472 Delay: 1 * time.Second, 473 Mode: structs.RestartPolicyModeFail, 474 } 475 476 ctx := testTaskRunnerFromAlloc(t, true, alloc) 477 ctx.tr.MarkReceived() 478 go ctx.tr.Run() 479 defer ctx.Cleanup() 480 481 select { 482 case <-ctx.tr.WaitCh(): 483 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 484 t.Fatalf("timeout") 485 } 486 487 if len(ctx.upd.events) != 8 { 488 t.Fatalf("should have 8 ctx.updates: %#v", ctx.upd.events) 489 } 490 491 if ctx.upd.state != structs.TaskStateDead { 492 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 493 } 494 495 if ctx.upd.events[0].Type != structs.TaskReceived { 496 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 497 } 498 499 if ctx.upd.events[1].Type != structs.TaskSetup { 500 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 501 } 502 503 if ctx.upd.events[2].Type != structs.TaskDownloadingArtifacts { 504 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskDownloadingArtifacts) 505 } 506 507 if ctx.upd.events[3].Type != structs.TaskArtifactDownloadFailed { 508 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskArtifactDownloadFailed) 509 } 510 511 if ctx.upd.events[4].Type != structs.TaskRestarting { 512 t.Fatalf("Fifth Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskRestarting) 513 } 514 515 if ctx.upd.events[5].Type != structs.TaskDownloadingArtifacts { 516 t.Fatalf("Sixth Event was %v; want %v", ctx.upd.events[5].Type, structs.TaskDownloadingArtifacts) 517 } 518 519 if ctx.upd.events[6].Type != structs.TaskArtifactDownloadFailed { 520 t.Fatalf("Seventh Event was %v; want %v", ctx.upd.events[6].Type, structs.TaskArtifactDownloadFailed) 521 } 522 523 if ctx.upd.events[7].Type != structs.TaskNotRestarting { 524 t.Fatalf("Eighth Event was %v; want %v", ctx.upd.events[7].Type, structs.TaskNotRestarting) 525 } 526 } 527 528 func TestTaskRunner_Validate_UserEnforcement(t *testing.T) { 529 ctestutil.ExecCompatible(t) 530 ctx := testTaskRunner(t, false) 531 defer ctx.Cleanup() 532 533 if err := ctx.tr.setTaskEnv(); err != nil { 534 t.Fatalf("bad: %v", err) 535 } 536 537 // Try to run as root with exec. 538 ctx.tr.task.Driver = "exec" 539 ctx.tr.task.User = "root" 540 if err := ctx.tr.validateTask(); err == nil { 541 t.Fatalf("expected error running as root with exec") 542 } 543 544 // Try to run a non-blacklisted user with exec. 545 ctx.tr.task.Driver = "exec" 546 ctx.tr.task.User = "foobar" 547 if err := ctx.tr.validateTask(); err != nil { 548 t.Fatalf("unexpected error: %v", err) 549 } 550 551 // Try to run as root with docker. 552 ctx.tr.task.Driver = "docker" 553 ctx.tr.task.User = "root" 554 if err := ctx.tr.validateTask(); err != nil { 555 t.Fatalf("unexpected error: %v", err) 556 } 557 } 558 559 func TestTaskRunner_RestartTask(t *testing.T) { 560 alloc := mock.Alloc() 561 task := alloc.Job.TaskGroups[0].Tasks[0] 562 task.Driver = "mock_driver" 563 task.Config = map[string]interface{}{ 564 "exit_code": "0", 565 "run_for": "100s", 566 } 567 568 ctx := testTaskRunnerFromAlloc(t, true, alloc) 569 ctx.tr.MarkReceived() 570 go ctx.tr.Run() 571 defer ctx.Cleanup() 572 573 // Wait for it to start 574 go func() { 575 testWaitForTaskToStart(t, ctx) 576 ctx.tr.Restart("test", "restart") 577 578 // Wait for it to restart then kill 579 go func() { 580 // Wait for the task to start again 581 testutil.WaitForResult(func() (bool, error) { 582 if len(ctx.upd.events) != 8 { 583 t.Fatalf("task %q in alloc %q should have 8 ctx.updates: %#v", task.Name, alloc.ID, ctx.upd.events) 584 } 585 586 return true, nil 587 }, func(err error) { 588 t.Fatalf("err: %v", err) 589 }) 590 ctx.tr.Kill("test", "restart", false) 591 }() 592 }() 593 594 select { 595 case <-ctx.tr.WaitCh(): 596 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 597 t.Fatalf("timeout") 598 } 599 600 if len(ctx.upd.events) != 10 { 601 t.Fatalf("should have 9 ctx.updates: %#v", ctx.upd.events) 602 } 603 604 if ctx.upd.state != structs.TaskStateDead { 605 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 606 } 607 608 if ctx.upd.events[0].Type != structs.TaskReceived { 609 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 610 } 611 612 if ctx.upd.events[1].Type != structs.TaskSetup { 613 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 614 } 615 616 if ctx.upd.events[2].Type != structs.TaskStarted { 617 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 618 } 619 620 if ctx.upd.events[3].Type != structs.TaskRestartSignal { 621 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskRestartSignal) 622 } 623 624 if ctx.upd.events[4].Type != structs.TaskKilling { 625 t.Fatalf("Fifth Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskKilling) 626 } 627 628 if ctx.upd.events[5].Type != structs.TaskKilled { 629 t.Fatalf("Sixth Event was %v; want %v", ctx.upd.events[5].Type, structs.TaskKilled) 630 } 631 632 if ctx.upd.events[6].Type != structs.TaskRestarting { 633 t.Fatalf("Seventh Event was %v; want %v", ctx.upd.events[6].Type, structs.TaskRestarting) 634 } 635 636 if ctx.upd.events[7].Type != structs.TaskStarted { 637 t.Fatalf("Eighth Event was %v; want %v", ctx.upd.events[8].Type, structs.TaskStarted) 638 } 639 if ctx.upd.events[8].Type != structs.TaskKilling { 640 t.Fatalf("Nineth Event was %v; want %v", ctx.upd.events[8].Type, structs.TaskKilling) 641 } 642 643 if ctx.upd.events[9].Type != structs.TaskKilled { 644 t.Fatalf("Tenth Event was %v; want %v", ctx.upd.events[9].Type, structs.TaskKilled) 645 } 646 } 647 648 func TestTaskRunner_KillTask(t *testing.T) { 649 alloc := mock.Alloc() 650 task := alloc.Job.TaskGroups[0].Tasks[0] 651 task.Driver = "mock_driver" 652 task.Config = map[string]interface{}{ 653 "exit_code": "0", 654 "run_for": "10s", 655 } 656 657 ctx := testTaskRunnerFromAlloc(t, false, alloc) 658 ctx.tr.MarkReceived() 659 go ctx.tr.Run() 660 defer ctx.Cleanup() 661 662 go func() { 663 testWaitForTaskToStart(t, ctx) 664 ctx.tr.Kill("test", "kill", true) 665 }() 666 667 select { 668 case <-ctx.tr.WaitCh(): 669 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 670 t.Fatalf("timeout") 671 } 672 673 if len(ctx.upd.events) != 5 { 674 t.Fatalf("should have 4 ctx.updates: %#v", ctx.upd.events) 675 } 676 677 if ctx.upd.state != structs.TaskStateDead { 678 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 679 } 680 681 if !ctx.upd.failed { 682 t.Fatalf("TaskState should be failed: %+v", ctx.upd) 683 } 684 685 if ctx.upd.events[0].Type != structs.TaskReceived { 686 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 687 } 688 689 if ctx.upd.events[1].Type != structs.TaskSetup { 690 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 691 } 692 693 if ctx.upd.events[2].Type != structs.TaskStarted { 694 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 695 } 696 697 if ctx.upd.events[3].Type != structs.TaskKilling { 698 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskKilling) 699 } 700 701 if ctx.upd.events[4].Type != structs.TaskKilled { 702 t.Fatalf("Fifth Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskKilled) 703 } 704 } 705 706 func TestTaskRunner_SignalFailure(t *testing.T) { 707 alloc := mock.Alloc() 708 task := alloc.Job.TaskGroups[0].Tasks[0] 709 task.Driver = "mock_driver" 710 task.Config = map[string]interface{}{ 711 "exit_code": "0", 712 "run_for": "10s", 713 "signal_error": "test forcing failure", 714 } 715 716 ctx := testTaskRunnerFromAlloc(t, false, alloc) 717 ctx.tr.MarkReceived() 718 go ctx.tr.Run() 719 defer ctx.Cleanup() 720 721 // Wait for the task to start 722 testWaitForTaskToStart(t, ctx) 723 724 if err := ctx.tr.Signal("test", "test", syscall.SIGINT); err == nil { 725 t.Fatalf("Didn't receive error") 726 } 727 } 728 729 func TestTaskRunner_BlockForVault(t *testing.T) { 730 alloc := mock.Alloc() 731 task := alloc.Job.TaskGroups[0].Tasks[0] 732 task.Driver = "mock_driver" 733 task.Config = map[string]interface{}{ 734 "exit_code": "0", 735 "run_for": "1s", 736 } 737 task.Vault = &structs.Vault{Policies: []string{"default"}} 738 739 ctx := testTaskRunnerFromAlloc(t, false, alloc) 740 ctx.tr.MarkReceived() 741 defer ctx.Cleanup() 742 743 // Control when we get a Vault token 744 token := "1234" 745 waitCh := make(chan struct{}) 746 handler := func(*structs.Allocation, []string) (map[string]string, error) { 747 <-waitCh 748 return map[string]string{task.Name: token}, nil 749 } 750 ctx.tr.vaultClient.(*vaultclient.MockVaultClient).DeriveTokenFn = handler 751 752 go ctx.tr.Run() 753 754 select { 755 case <-ctx.tr.WaitCh(): 756 t.Fatalf("premature exit") 757 case <-time.After(1 * time.Second): 758 } 759 760 if len(ctx.upd.events) != 2 { 761 t.Fatalf("should have 2 ctx.updates: %#v", ctx.upd.events) 762 } 763 764 if ctx.upd.state != structs.TaskStatePending { 765 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStatePending) 766 } 767 768 if ctx.upd.events[0].Type != structs.TaskReceived { 769 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 770 } 771 772 if ctx.upd.events[1].Type != structs.TaskSetup { 773 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 774 } 775 776 // Unblock 777 close(waitCh) 778 779 select { 780 case <-ctx.tr.WaitCh(): 781 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 782 t.Fatalf("timeout") 783 } 784 785 if len(ctx.upd.events) != 4 { 786 t.Fatalf("should have 4 ctx.updates: %#v", ctx.upd.events) 787 } 788 789 if ctx.upd.state != structs.TaskStateDead { 790 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 791 } 792 793 if ctx.upd.events[0].Type != structs.TaskReceived { 794 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 795 } 796 797 if ctx.upd.events[1].Type != structs.TaskSetup { 798 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 799 } 800 801 if ctx.upd.events[2].Type != structs.TaskStarted { 802 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 803 } 804 805 if ctx.upd.events[3].Type != structs.TaskTerminated { 806 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskTerminated) 807 } 808 809 // Check that the token is on disk 810 tokenPath := filepath.Join(ctx.tr.taskDir.SecretsDir, vaultTokenFile) 811 data, err := ioutil.ReadFile(tokenPath) 812 if err != nil { 813 t.Fatalf("Failed to read file: %v", err) 814 } 815 816 if act := string(data); act != token { 817 t.Fatalf("Token didn't get written to disk properly, got %q; want %q", act, token) 818 } 819 820 // Check the token was revoked 821 m := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 822 testutil.WaitForResult(func() (bool, error) { 823 if len(m.StoppedTokens) != 1 { 824 return false, fmt.Errorf("Expected a stopped token: %v", m.StoppedTokens) 825 } 826 827 if a := m.StoppedTokens[0]; a != token { 828 return false, fmt.Errorf("got stopped token %q; want %q", a, token) 829 } 830 return true, nil 831 }, func(err error) { 832 t.Fatalf("err: %v", err) 833 }) 834 } 835 836 func TestTaskRunner_DeriveToken_Retry(t *testing.T) { 837 alloc := mock.Alloc() 838 task := alloc.Job.TaskGroups[0].Tasks[0] 839 task.Driver = "mock_driver" 840 task.Config = map[string]interface{}{ 841 "exit_code": "0", 842 "run_for": "1s", 843 } 844 task.Vault = &structs.Vault{Policies: []string{"default"}} 845 846 ctx := testTaskRunnerFromAlloc(t, false, alloc) 847 ctx.tr.MarkReceived() 848 defer ctx.Cleanup() 849 850 // Control when we get a Vault token 851 token := "1234" 852 count := 0 853 handler := func(*structs.Allocation, []string) (map[string]string, error) { 854 if count > 0 { 855 return map[string]string{task.Name: token}, nil 856 } 857 858 count++ 859 return nil, structs.NewRecoverableError(fmt.Errorf("Want a retry"), true) 860 } 861 ctx.tr.vaultClient.(*vaultclient.MockVaultClient).DeriveTokenFn = handler 862 go ctx.tr.Run() 863 864 select { 865 case <-ctx.tr.WaitCh(): 866 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 867 t.Fatalf("timeout") 868 } 869 870 if len(ctx.upd.events) != 4 { 871 t.Fatalf("should have 4 ctx.updates: %#v", ctx.upd.events) 872 } 873 874 if ctx.upd.state != structs.TaskStateDead { 875 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 876 } 877 878 if ctx.upd.events[0].Type != structs.TaskReceived { 879 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 880 } 881 882 if ctx.upd.events[1].Type != structs.TaskSetup { 883 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 884 } 885 886 if ctx.upd.events[2].Type != structs.TaskStarted { 887 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 888 } 889 890 if ctx.upd.events[3].Type != structs.TaskTerminated { 891 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskTerminated) 892 } 893 894 // Check that the token is on disk 895 tokenPath := filepath.Join(ctx.tr.taskDir.SecretsDir, vaultTokenFile) 896 data, err := ioutil.ReadFile(tokenPath) 897 if err != nil { 898 t.Fatalf("Failed to read file: %v", err) 899 } 900 901 if act := string(data); act != token { 902 t.Fatalf("Token didn't get written to disk properly, got %q; want %q", act, token) 903 } 904 905 // Check the token was revoked 906 m := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 907 testutil.WaitForResult(func() (bool, error) { 908 if len(m.StoppedTokens) != 1 { 909 return false, fmt.Errorf("Expected a stopped token: %v", m.StoppedTokens) 910 } 911 912 if a := m.StoppedTokens[0]; a != token { 913 return false, fmt.Errorf("got stopped token %q; want %q", a, token) 914 } 915 return true, nil 916 }, func(err error) { 917 t.Fatalf("err: %v", err) 918 }) 919 } 920 921 func TestTaskRunner_DeriveToken_Unrecoverable(t *testing.T) { 922 alloc := mock.Alloc() 923 task := alloc.Job.TaskGroups[0].Tasks[0] 924 task.Driver = "mock_driver" 925 task.Config = map[string]interface{}{ 926 "exit_code": "0", 927 "run_for": "10s", 928 } 929 task.Vault = &structs.Vault{ 930 Policies: []string{"default"}, 931 ChangeMode: structs.VaultChangeModeRestart, 932 } 933 934 ctx := testTaskRunnerFromAlloc(t, false, alloc) 935 ctx.tr.MarkReceived() 936 defer ctx.Cleanup() 937 938 // Error the token derivation 939 vc := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 940 vc.SetDeriveTokenError(alloc.ID, []string{task.Name}, fmt.Errorf("Non recoverable")) 941 go ctx.tr.Run() 942 943 // Wait for the task to start 944 testutil.WaitForResult(func() (bool, error) { 945 if l := len(ctx.upd.events); l != 3 { 946 return false, fmt.Errorf("Expect 3 events; got %v", l) 947 } 948 949 if ctx.upd.events[0].Type != structs.TaskReceived { 950 return false, fmt.Errorf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 951 } 952 953 if ctx.upd.events[1].Type != structs.TaskSetup { 954 return false, fmt.Errorf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 955 } 956 957 if ctx.upd.events[2].Type != structs.TaskKilling { 958 return false, fmt.Errorf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskKilling) 959 } 960 961 return true, nil 962 }, func(err error) { 963 t.Fatalf("err: %v", err) 964 }) 965 } 966 967 func TestTaskRunner_Template_Block(t *testing.T) { 968 testRetryRate = 2 * time.Second 969 defer func() { 970 testRetryRate = 0 971 }() 972 alloc := mock.Alloc() 973 task := alloc.Job.TaskGroups[0].Tasks[0] 974 task.Driver = "mock_driver" 975 task.Config = map[string]interface{}{ 976 "exit_code": "0", 977 "run_for": "1s", 978 } 979 task.Templates = []*structs.Template{ 980 { 981 EmbeddedTmpl: "{{key \"foo\"}}", 982 DestPath: "local/test", 983 ChangeMode: structs.TemplateChangeModeNoop, 984 }, 985 } 986 987 ctx := testTaskRunnerFromAlloc(t, false, alloc) 988 ctx.tr.MarkReceived() 989 go ctx.tr.Run() 990 defer ctx.Cleanup() 991 992 select { 993 case <-ctx.tr.WaitCh(): 994 t.Fatalf("premature exit") 995 case <-time.After(1 * time.Second): 996 } 997 998 if len(ctx.upd.events) != 2 { 999 t.Fatalf("should have 2 ctx.updates: %#v", ctx.upd.events) 1000 } 1001 1002 if ctx.upd.state != structs.TaskStatePending { 1003 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStatePending) 1004 } 1005 1006 if ctx.upd.events[0].Type != structs.TaskReceived { 1007 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 1008 } 1009 1010 if ctx.upd.events[1].Type != structs.TaskSetup { 1011 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 1012 } 1013 1014 // Unblock 1015 ctx.tr.UnblockStart("test") 1016 1017 select { 1018 case <-ctx.tr.WaitCh(): 1019 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 1020 t.Fatalf("timeout") 1021 } 1022 1023 if len(ctx.upd.events) != 4 { 1024 t.Fatalf("should have 4 ctx.updates: %#v", ctx.upd.events) 1025 } 1026 1027 if ctx.upd.state != structs.TaskStateDead { 1028 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 1029 } 1030 1031 if ctx.upd.events[0].Type != structs.TaskReceived { 1032 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 1033 } 1034 1035 if ctx.upd.events[1].Type != structs.TaskSetup { 1036 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 1037 } 1038 1039 if ctx.upd.events[2].Type != structs.TaskStarted { 1040 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 1041 } 1042 1043 if ctx.upd.events[3].Type != structs.TaskTerminated { 1044 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskTerminated) 1045 } 1046 } 1047 1048 func TestTaskRunner_Template_Artifact(t *testing.T) { 1049 dir, err := os.Getwd() 1050 if err != nil { 1051 t.Fatalf("bad: %v", err) 1052 } 1053 1054 ts := httptest.NewServer(http.FileServer(http.Dir(filepath.Join(dir, "..")))) 1055 defer ts.Close() 1056 1057 alloc := mock.Alloc() 1058 task := alloc.Job.TaskGroups[0].Tasks[0] 1059 task.Driver = "mock_driver" 1060 task.Config = map[string]interface{}{ 1061 "exit_code": "0", 1062 "run_for": "1s", 1063 } 1064 // Create an allocation that has a task that renders a template from an 1065 // artifact 1066 f1 := "CHANGELOG.md" 1067 artifact := structs.TaskArtifact{ 1068 GetterSource: fmt.Sprintf("%s/%s", ts.URL, f1), 1069 } 1070 task.Artifacts = []*structs.TaskArtifact{&artifact} 1071 task.Templates = []*structs.Template{ 1072 { 1073 SourcePath: "CHANGELOG.md", 1074 DestPath: "local/test", 1075 ChangeMode: structs.TemplateChangeModeNoop, 1076 }, 1077 } 1078 1079 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1080 ctx.tr.MarkReceived() 1081 defer ctx.Cleanup() 1082 go ctx.tr.Run() 1083 1084 select { 1085 case <-ctx.tr.WaitCh(): 1086 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 1087 t.Fatalf("timeout") 1088 } 1089 1090 if len(ctx.upd.events) != 5 { 1091 t.Fatalf("should have 5 ctx.updates: %#v", ctx.upd.events) 1092 } 1093 1094 if ctx.upd.state != structs.TaskStateDead { 1095 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 1096 } 1097 1098 if ctx.upd.events[0].Type != structs.TaskReceived { 1099 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 1100 } 1101 1102 if ctx.upd.events[1].Type != structs.TaskSetup { 1103 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 1104 } 1105 1106 if ctx.upd.events[2].Type != structs.TaskDownloadingArtifacts { 1107 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskDownloadingArtifacts) 1108 } 1109 1110 if ctx.upd.events[3].Type != structs.TaskStarted { 1111 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskStarted) 1112 } 1113 1114 if ctx.upd.events[4].Type != structs.TaskTerminated { 1115 t.Fatalf("Fifth Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskTerminated) 1116 } 1117 1118 // Check that both files exist. 1119 if _, err := os.Stat(filepath.Join(ctx.tr.taskDir.Dir, f1)); err != nil { 1120 t.Fatalf("%v not downloaded", f1) 1121 } 1122 if _, err := os.Stat(filepath.Join(ctx.tr.taskDir.LocalDir, "test")); err != nil { 1123 t.Fatalf("template not rendered") 1124 } 1125 } 1126 1127 func TestTaskRunner_Template_NewVaultToken(t *testing.T) { 1128 alloc := mock.Alloc() 1129 task := alloc.Job.TaskGroups[0].Tasks[0] 1130 task.Driver = "mock_driver" 1131 task.Config = map[string]interface{}{ 1132 "exit_code": "0", 1133 "run_for": "1s", 1134 } 1135 task.Templates = []*structs.Template{ 1136 { 1137 EmbeddedTmpl: "{{key \"foo\"}}", 1138 DestPath: "local/test", 1139 ChangeMode: structs.TemplateChangeModeNoop, 1140 }, 1141 } 1142 task.Vault = &structs.Vault{Policies: []string{"default"}} 1143 1144 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1145 ctx.tr.MarkReceived() 1146 defer ctx.Cleanup() 1147 go ctx.tr.Run() 1148 1149 // Wait for a Vault token 1150 var token string 1151 testutil.WaitForResult(func() (bool, error) { 1152 if token = ctx.tr.vaultFuture.Get(); token == "" { 1153 return false, fmt.Errorf("No Vault token") 1154 } 1155 1156 return true, nil 1157 }, func(err error) { 1158 t.Fatalf("err: %v", err) 1159 }) 1160 1161 // Error the token renewal 1162 vc := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 1163 renewalCh, ok := vc.RenewTokens[token] 1164 if !ok { 1165 t.Fatalf("no renewal channel") 1166 } 1167 1168 originalManager := ctx.tr.templateManager 1169 1170 renewalCh <- fmt.Errorf("Test killing") 1171 close(renewalCh) 1172 1173 // Wait for a new Vault token 1174 var token2 string 1175 testutil.WaitForResult(func() (bool, error) { 1176 if token2 = ctx.tr.vaultFuture.Get(); token2 == "" || token2 == token { 1177 return false, fmt.Errorf("No new Vault token") 1178 } 1179 1180 if originalManager == ctx.tr.templateManager { 1181 return false, fmt.Errorf("Template manager not ctx.updated") 1182 } 1183 1184 return true, nil 1185 }, func(err error) { 1186 t.Fatalf("err: %v", err) 1187 }) 1188 1189 // Check the token was revoked 1190 m := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 1191 testutil.WaitForResult(func() (bool, error) { 1192 if len(m.StoppedTokens) != 1 { 1193 return false, fmt.Errorf("Expected a stopped token: %v", m.StoppedTokens) 1194 } 1195 1196 if a := m.StoppedTokens[0]; a != token { 1197 return false, fmt.Errorf("got stopped token %q; want %q", a, token) 1198 } 1199 return true, nil 1200 }, func(err error) { 1201 t.Fatalf("err: %v", err) 1202 }) 1203 } 1204 1205 func TestTaskRunner_VaultManager_Restart(t *testing.T) { 1206 alloc := mock.Alloc() 1207 task := alloc.Job.TaskGroups[0].Tasks[0] 1208 task.Driver = "mock_driver" 1209 task.Config = map[string]interface{}{ 1210 "exit_code": "0", 1211 "run_for": "10s", 1212 } 1213 task.Vault = &structs.Vault{ 1214 Policies: []string{"default"}, 1215 ChangeMode: structs.VaultChangeModeRestart, 1216 } 1217 1218 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1219 ctx.tr.MarkReceived() 1220 defer ctx.Cleanup() 1221 go ctx.tr.Run() 1222 1223 // Wait for the task to start 1224 testWaitForTaskToStart(t, ctx) 1225 1226 // Error the token renewal 1227 vc := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 1228 renewalCh, ok := vc.RenewTokens[ctx.tr.vaultFuture.Get()] 1229 if !ok { 1230 t.Fatalf("no renewal channel") 1231 } 1232 1233 renewalCh <- fmt.Errorf("Test killing") 1234 close(renewalCh) 1235 1236 // Ensure a restart 1237 testutil.WaitForResult(func() (bool, error) { 1238 if l := len(ctx.upd.events); l != 8 { 1239 return false, fmt.Errorf("Expect eight events; got %#v", ctx.upd.events) 1240 } 1241 1242 if ctx.upd.events[0].Type != structs.TaskReceived { 1243 return false, fmt.Errorf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 1244 } 1245 1246 if ctx.upd.events[1].Type != structs.TaskSetup { 1247 return false, fmt.Errorf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskStarted) 1248 } 1249 1250 if ctx.upd.events[2].Type != structs.TaskStarted { 1251 return false, fmt.Errorf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 1252 } 1253 1254 if ctx.upd.events[3].Type != structs.TaskRestartSignal { 1255 return false, fmt.Errorf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskRestartSignal) 1256 } 1257 1258 if ctx.upd.events[4].Type != structs.TaskKilling { 1259 return false, fmt.Errorf("Fifth Event was %v; want %v", ctx.upd.events[4].Type, structs.TaskKilling) 1260 } 1261 1262 if ctx.upd.events[5].Type != structs.TaskKilled { 1263 return false, fmt.Errorf("Sixth Event was %v; want %v", ctx.upd.events[5].Type, structs.TaskKilled) 1264 } 1265 1266 if ctx.upd.events[6].Type != structs.TaskRestarting { 1267 return false, fmt.Errorf("Seventh Event was %v; want %v", ctx.upd.events[6].Type, structs.TaskRestarting) 1268 } 1269 1270 if ctx.upd.events[7].Type != structs.TaskStarted { 1271 return false, fmt.Errorf("Eight Event was %v; want %v", ctx.upd.events[7].Type, structs.TaskStarted) 1272 } 1273 1274 return true, nil 1275 }, func(err error) { 1276 t.Fatalf("err: %v", err) 1277 }) 1278 } 1279 1280 func TestTaskRunner_VaultManager_Signal(t *testing.T) { 1281 alloc := mock.Alloc() 1282 task := alloc.Job.TaskGroups[0].Tasks[0] 1283 task.Driver = "mock_driver" 1284 task.Config = map[string]interface{}{ 1285 "exit_code": "0", 1286 "run_for": "10s", 1287 } 1288 task.Vault = &structs.Vault{ 1289 Policies: []string{"default"}, 1290 ChangeMode: structs.VaultChangeModeSignal, 1291 ChangeSignal: "SIGUSR1", 1292 } 1293 1294 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1295 ctx.tr.MarkReceived() 1296 go ctx.tr.Run() 1297 defer ctx.Cleanup() 1298 1299 // Wait for the task to start 1300 testWaitForTaskToStart(t, ctx) 1301 1302 // Error the token renewal 1303 vc := ctx.tr.vaultClient.(*vaultclient.MockVaultClient) 1304 renewalCh, ok := vc.RenewTokens[ctx.tr.vaultFuture.Get()] 1305 if !ok { 1306 t.Fatalf("no renewal channel") 1307 } 1308 1309 renewalCh <- fmt.Errorf("Test killing") 1310 close(renewalCh) 1311 1312 // Ensure a restart 1313 testutil.WaitForResult(func() (bool, error) { 1314 if l := len(ctx.upd.events); l != 4 { 1315 return false, fmt.Errorf("Expect four events; got %#v", ctx.upd.events) 1316 } 1317 1318 if ctx.upd.events[0].Type != structs.TaskReceived { 1319 return false, fmt.Errorf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 1320 } 1321 1322 if ctx.upd.events[1].Type != structs.TaskSetup { 1323 return false, fmt.Errorf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 1324 } 1325 1326 if ctx.upd.events[2].Type != structs.TaskStarted { 1327 return false, fmt.Errorf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 1328 } 1329 1330 if ctx.upd.events[3].Type != structs.TaskSignaling { 1331 return false, fmt.Errorf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskSignaling) 1332 } 1333 1334 return true, nil 1335 }, func(err error) { 1336 t.Fatalf("err: %v", err) 1337 }) 1338 } 1339 1340 // Test that the payload is written to disk 1341 func TestTaskRunner_SimpleRun_Dispatch(t *testing.T) { 1342 alloc := mock.Alloc() 1343 task := alloc.Job.TaskGroups[0].Tasks[0] 1344 task.Driver = "mock_driver" 1345 task.Config = map[string]interface{}{ 1346 "exit_code": "0", 1347 "run_for": "1s", 1348 } 1349 fileName := "test" 1350 task.DispatchPayload = &structs.DispatchPayloadConfig{ 1351 File: fileName, 1352 } 1353 alloc.Job.ParameterizedJob = &structs.ParameterizedJobConfig{} 1354 1355 // Add an encrypted payload 1356 expected := []byte("hello world") 1357 compressed := snappy.Encode(nil, expected) 1358 alloc.Job.Payload = compressed 1359 1360 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1361 ctx.tr.MarkReceived() 1362 defer ctx.tr.Destroy(structs.NewTaskEvent(structs.TaskKilled)) 1363 defer ctx.allocDir.Destroy() 1364 go ctx.tr.Run() 1365 1366 select { 1367 case <-ctx.tr.WaitCh(): 1368 case <-time.After(time.Duration(testutil.TestMultiplier()*15) * time.Second): 1369 t.Fatalf("timeout") 1370 } 1371 1372 if len(ctx.upd.events) != 4 { 1373 t.Fatalf("should have 4 updates: %#v", ctx.upd.events) 1374 } 1375 1376 if ctx.upd.state != structs.TaskStateDead { 1377 t.Fatalf("TaskState %v; want %v", ctx.upd.state, structs.TaskStateDead) 1378 } 1379 1380 if ctx.upd.events[0].Type != structs.TaskReceived { 1381 t.Fatalf("First Event was %v; want %v", ctx.upd.events[0].Type, structs.TaskReceived) 1382 } 1383 1384 if ctx.upd.events[1].Type != structs.TaskSetup { 1385 t.Fatalf("Second Event was %v; want %v", ctx.upd.events[1].Type, structs.TaskSetup) 1386 } 1387 1388 if ctx.upd.events[2].Type != structs.TaskStarted { 1389 t.Fatalf("Third Event was %v; want %v", ctx.upd.events[2].Type, structs.TaskStarted) 1390 } 1391 1392 if ctx.upd.events[3].Type != structs.TaskTerminated { 1393 t.Fatalf("Fourth Event was %v; want %v", ctx.upd.events[3].Type, structs.TaskTerminated) 1394 } 1395 1396 // Check that the file was written to disk properly 1397 payloadPath := filepath.Join(ctx.tr.taskDir.LocalDir, fileName) 1398 data, err := ioutil.ReadFile(payloadPath) 1399 if err != nil { 1400 t.Fatalf("Failed to read file: %v", err) 1401 } 1402 if !reflect.DeepEqual(data, expected) { 1403 t.Fatalf("Bad; got %v; want %v", string(data), string(expected)) 1404 } 1405 } 1406 1407 // TestTaskRunner_CleanupEmpty ensures TaskRunner works when createdResources 1408 // is empty. 1409 func TestTaskRunner_CleanupEmpty(t *testing.T) { 1410 alloc := mock.Alloc() 1411 task := alloc.Job.TaskGroups[0].Tasks[0] 1412 task.Driver = "mock_driver" 1413 1414 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1415 ctx.tr.MarkReceived() 1416 1417 defer ctx.Cleanup() 1418 ctx.tr.Run() 1419 1420 // Since we only failed once, createdResources should be empty 1421 if len(ctx.tr.createdResources.Resources) != 0 { 1422 t.Fatalf("createdResources should still be empty: %v", ctx.tr.createdResources) 1423 } 1424 } 1425 1426 func TestTaskRunner_CleanupOK(t *testing.T) { 1427 alloc := mock.Alloc() 1428 task := alloc.Job.TaskGroups[0].Tasks[0] 1429 task.Driver = "mock_driver" 1430 key := "ERR" 1431 1432 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1433 ctx.tr.config.Options = map[string]string{ 1434 "cleanup_fail_on": key, 1435 "cleanup_fail_num": "1", 1436 } 1437 ctx.tr.MarkReceived() 1438 1439 ctx.tr.createdResources.Resources[key] = []string{"x", "y"} 1440 ctx.tr.createdResources.Resources["foo"] = []string{"z"} 1441 1442 defer ctx.Cleanup() 1443 ctx.tr.Run() 1444 1445 // Since we only failed once, createdResources should be empty 1446 if len(ctx.tr.createdResources.Resources) > 0 { 1447 t.Fatalf("expected all created resources to be removed: %#v", ctx.tr.createdResources.Resources) 1448 } 1449 } 1450 1451 func TestTaskRunner_CleanupFail(t *testing.T) { 1452 alloc := mock.Alloc() 1453 task := alloc.Job.TaskGroups[0].Tasks[0] 1454 task.Driver = "mock_driver" 1455 key := "ERR" 1456 ctx := testTaskRunnerFromAlloc(t, false, alloc) 1457 ctx.tr.config.Options = map[string]string{ 1458 "cleanup_fail_on": key, 1459 "cleanup_fail_num": "5", 1460 } 1461 ctx.tr.MarkReceived() 1462 1463 ctx.tr.createdResources.Resources[key] = []string{"x"} 1464 ctx.tr.createdResources.Resources["foo"] = []string{"y", "z"} 1465 1466 defer ctx.Cleanup() 1467 ctx.tr.Run() 1468 1469 // Since we failed > 3 times, the failed key should remain 1470 expected := map[string][]string{key: {"x"}} 1471 if !reflect.DeepEqual(expected, ctx.tr.createdResources.Resources) { 1472 t.Fatalf("expected %#v but found: %#v", expected, ctx.tr.createdResources.Resources) 1473 } 1474 }