github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/drivers/exec/driver_test.go (about) 1 package exec 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "regexp" 11 "runtime" 12 "strconv" 13 "strings" 14 "sync" 15 "syscall" 16 "testing" 17 "time" 18 19 ctestutils "github.com/hashicorp/nomad/client/testutil" 20 "github.com/hashicorp/nomad/helper/pluginutils/hclutils" 21 "github.com/hashicorp/nomad/helper/testlog" 22 "github.com/hashicorp/nomad/helper/testtask" 23 "github.com/hashicorp/nomad/helper/uuid" 24 "github.com/hashicorp/nomad/nomad/structs" 25 basePlug "github.com/hashicorp/nomad/plugins/base" 26 "github.com/hashicorp/nomad/plugins/drivers" 27 dtestutil "github.com/hashicorp/nomad/plugins/drivers/testutils" 28 "github.com/hashicorp/nomad/testutil" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestMain(m *testing.M) { 33 if !testtask.Run() { 34 os.Exit(m.Run()) 35 } 36 } 37 38 var testResources = &drivers.Resources{ 39 NomadResources: &structs.AllocatedTaskResources{ 40 Memory: structs.AllocatedMemoryResources{ 41 MemoryMB: 128, 42 }, 43 Cpu: structs.AllocatedCpuResources{ 44 CpuShares: 100, 45 }, 46 }, 47 LinuxResources: &drivers.LinuxResources{ 48 MemoryLimitBytes: 134217728, 49 CPUShares: 100, 50 }, 51 } 52 53 func TestExecDriver_Fingerprint_NonLinux(t *testing.T) { 54 if !testutil.IsCI() { 55 t.Parallel() 56 } 57 require := require.New(t) 58 if runtime.GOOS == "linux" { 59 t.Skip("Test only available not on Linux") 60 } 61 62 d := NewExecDriver(testlog.HCLogger(t)) 63 harness := dtestutil.NewDriverHarness(t, d) 64 65 fingerCh, err := harness.Fingerprint(context.Background()) 66 require.NoError(err) 67 select { 68 case finger := <-fingerCh: 69 require.Equal(drivers.HealthStateUndetected, finger.Health) 70 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 71 require.Fail("timeout receiving fingerprint") 72 } 73 } 74 75 func TestExecDriver_Fingerprint(t *testing.T) { 76 t.Parallel() 77 require := require.New(t) 78 79 ctestutils.ExecCompatible(t) 80 81 d := NewExecDriver(testlog.HCLogger(t)) 82 harness := dtestutil.NewDriverHarness(t, d) 83 84 fingerCh, err := harness.Fingerprint(context.Background()) 85 require.NoError(err) 86 select { 87 case finger := <-fingerCh: 88 require.Equal(drivers.HealthStateHealthy, finger.Health) 89 require.True(finger.Attributes["driver.exec"].GetBool()) 90 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 91 require.Fail("timeout receiving fingerprint") 92 } 93 } 94 95 func TestExecDriver_StartWait(t *testing.T) { 96 t.Parallel() 97 require := require.New(t) 98 ctestutils.ExecCompatible(t) 99 100 d := NewExecDriver(testlog.HCLogger(t)) 101 harness := dtestutil.NewDriverHarness(t, d) 102 task := &drivers.TaskConfig{ 103 ID: uuid.Generate(), 104 Name: "test", 105 Resources: testResources, 106 } 107 108 tc := &TaskConfig{ 109 Command: "cat", 110 Args: []string{"/proc/self/cgroup"}, 111 } 112 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 113 114 cleanup := harness.MkAllocDir(task, false) 115 defer cleanup() 116 117 handle, _, err := harness.StartTask(task) 118 require.NoError(err) 119 120 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 121 require.NoError(err) 122 result := <-ch 123 require.Zero(result.ExitCode) 124 require.NoError(harness.DestroyTask(task.ID, true)) 125 } 126 127 func TestExecDriver_StartWaitStopKill(t *testing.T) { 128 t.Parallel() 129 require := require.New(t) 130 ctestutils.ExecCompatible(t) 131 132 d := NewExecDriver(testlog.HCLogger(t)) 133 harness := dtestutil.NewDriverHarness(t, d) 134 task := &drivers.TaskConfig{ 135 ID: uuid.Generate(), 136 Name: "test", 137 Resources: testResources, 138 } 139 140 tc := &TaskConfig{ 141 Command: "/bin/bash", 142 Args: []string{"-c", "echo hi; sleep 600"}, 143 } 144 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 145 146 cleanup := harness.MkAllocDir(task, false) 147 defer cleanup() 148 149 handle, _, err := harness.StartTask(task) 150 require.NoError(err) 151 defer harness.DestroyTask(task.ID, true) 152 153 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 154 require.NoError(err) 155 156 require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second)) 157 158 go func() { 159 harness.StopTask(task.ID, 2*time.Second, "SIGINT") 160 }() 161 162 select { 163 case result := <-ch: 164 require.False(result.Successful()) 165 case <-time.After(10 * time.Second): 166 require.Fail("timeout waiting for task to shutdown") 167 } 168 169 // Ensure that the task is marked as dead, but account 170 // for WaitTask() closing channel before internal state is updated 171 testutil.WaitForResult(func() (bool, error) { 172 status, err := harness.InspectTask(task.ID) 173 if err != nil { 174 return false, fmt.Errorf("inspecting task failed: %v", err) 175 } 176 if status.State != drivers.TaskStateExited { 177 return false, fmt.Errorf("task hasn't exited yet; status: %v", status.State) 178 } 179 180 return true, nil 181 }, func(err error) { 182 require.NoError(err) 183 }) 184 185 require.NoError(harness.DestroyTask(task.ID, true)) 186 } 187 188 func TestExecDriver_StartWaitRecover(t *testing.T) { 189 t.Parallel() 190 require := require.New(t) 191 ctestutils.ExecCompatible(t) 192 193 d := NewExecDriver(testlog.HCLogger(t)) 194 harness := dtestutil.NewDriverHarness(t, d) 195 task := &drivers.TaskConfig{ 196 ID: uuid.Generate(), 197 Name: "test", 198 Resources: testResources, 199 } 200 201 tc := &TaskConfig{ 202 Command: "/bin/sleep", 203 Args: []string{"5"}, 204 } 205 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 206 207 cleanup := harness.MkAllocDir(task, false) 208 defer cleanup() 209 210 handle, _, err := harness.StartTask(task) 211 require.NoError(err) 212 213 ctx, cancel := context.WithCancel(context.Background()) 214 215 ch, err := harness.WaitTask(ctx, handle.Config.ID) 216 require.NoError(err) 217 218 var wg sync.WaitGroup 219 wg.Add(1) 220 go func() { 221 defer wg.Done() 222 result := <-ch 223 require.Error(result.Err) 224 }() 225 226 require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second)) 227 cancel() 228 229 waitCh := make(chan struct{}) 230 go func() { 231 defer close(waitCh) 232 wg.Wait() 233 }() 234 235 select { 236 case <-waitCh: 237 status, err := harness.InspectTask(task.ID) 238 require.NoError(err) 239 require.Equal(drivers.TaskStateRunning, status.State) 240 case <-time.After(1 * time.Second): 241 require.Fail("timeout waiting for task wait to cancel") 242 } 243 244 // Loose task 245 d.(*Driver).tasks.Delete(task.ID) 246 _, err = harness.InspectTask(task.ID) 247 require.Error(err) 248 249 require.NoError(harness.RecoverTask(handle)) 250 status, err := harness.InspectTask(task.ID) 251 require.NoError(err) 252 require.Equal(drivers.TaskStateRunning, status.State) 253 254 require.NoError(harness.StopTask(task.ID, 0, "")) 255 require.NoError(harness.DestroyTask(task.ID, true)) 256 } 257 258 // TestExecDriver_DestroyKillsAll asserts that when TaskDestroy is called all 259 // task processes are cleaned up. 260 func TestExecDriver_DestroyKillsAll(t *testing.T) { 261 t.Parallel() 262 require := require.New(t) 263 ctestutils.ExecCompatible(t) 264 265 d := NewExecDriver(testlog.HCLogger(t)) 266 harness := dtestutil.NewDriverHarness(t, d) 267 defer harness.Kill() 268 269 task := &drivers.TaskConfig{ 270 ID: uuid.Generate(), 271 Name: "test", 272 } 273 274 cleanup := harness.MkAllocDir(task, true) 275 defer cleanup() 276 277 taskConfig := map[string]interface{}{} 278 taskConfig["command"] = "/bin/sh" 279 taskConfig["args"] = []string{"-c", fmt.Sprintf(`sleep 3600 & echo "SLEEP_PID=$!"`)} 280 281 require.NoError(task.EncodeConcreteDriverConfig(&taskConfig)) 282 283 handle, _, err := harness.StartTask(task) 284 require.NoError(err) 285 defer harness.DestroyTask(task.ID, true) 286 287 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 288 require.NoError(err) 289 290 select { 291 case result := <-ch: 292 require.True(result.Successful(), "command failed: %#v", result) 293 case <-time.After(10 * time.Second): 294 require.Fail("timeout waiting for task to shutdown") 295 } 296 297 sleepPid := 0 298 299 // Ensure that the task is marked as dead, but account 300 // for WaitTask() closing channel before internal state is updated 301 testutil.WaitForResult(func() (bool, error) { 302 stdout, err := ioutil.ReadFile(filepath.Join(task.TaskDir().LogDir, "test.stdout.0")) 303 if err != nil { 304 return false, fmt.Errorf("failed to output pid file: %v", err) 305 } 306 307 pidMatch := regexp.MustCompile(`SLEEP_PID=(\d+)`).FindStringSubmatch(string(stdout)) 308 if len(pidMatch) != 2 { 309 return false, fmt.Errorf("failed to find pid in %s", string(stdout)) 310 } 311 312 pid, err := strconv.Atoi(pidMatch[1]) 313 if err != nil { 314 return false, fmt.Errorf("pid parts aren't int: %s", pidMatch[1]) 315 } 316 317 sleepPid = pid 318 return true, nil 319 }, func(err error) { 320 require.NoError(err) 321 }) 322 323 // isProcessRunning returns an error if process is not running 324 isProcessRunning := func(pid int) error { 325 process, err := os.FindProcess(pid) 326 if err != nil { 327 return fmt.Errorf("failed to find process: %s", err) 328 } 329 330 err = process.Signal(syscall.Signal(0)) 331 if err != nil { 332 return fmt.Errorf("failed to signal process: %s", err) 333 } 334 335 return nil 336 } 337 338 require.NoError(isProcessRunning(sleepPid)) 339 340 require.NoError(harness.DestroyTask(task.ID, true)) 341 342 testutil.WaitForResult(func() (bool, error) { 343 err := isProcessRunning(sleepPid) 344 if err == nil { 345 return false, fmt.Errorf("child process is still running") 346 } 347 348 if !strings.Contains(err.Error(), "failed to signal process") { 349 return false, fmt.Errorf("unexpected error: %v", err) 350 } 351 352 return true, nil 353 }, func(err error) { 354 require.NoError(err) 355 }) 356 } 357 358 func TestExecDriver_Stats(t *testing.T) { 359 t.Parallel() 360 require := require.New(t) 361 ctestutils.ExecCompatible(t) 362 363 d := NewExecDriver(testlog.HCLogger(t)) 364 harness := dtestutil.NewDriverHarness(t, d) 365 task := &drivers.TaskConfig{ 366 ID: uuid.Generate(), 367 Name: "test", 368 Resources: testResources, 369 } 370 371 tc := &TaskConfig{ 372 Command: "/bin/sleep", 373 Args: []string{"5"}, 374 } 375 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 376 377 cleanup := harness.MkAllocDir(task, false) 378 defer cleanup() 379 380 handle, _, err := harness.StartTask(task) 381 require.NoError(err) 382 require.NotNil(handle) 383 384 require.NoError(harness.WaitUntilStarted(task.ID, 1*time.Second)) 385 ctx, cancel := context.WithCancel(context.Background()) 386 defer cancel() 387 statsCh, err := harness.TaskStats(ctx, task.ID, time.Second*10) 388 require.NoError(err) 389 select { 390 case stats := <-statsCh: 391 require.NotZero(stats.ResourceUsage.MemoryStats.RSS) 392 require.NotZero(stats.Timestamp) 393 require.WithinDuration(time.Now(), time.Unix(0, stats.Timestamp), time.Second) 394 case <-time.After(time.Second): 395 require.Fail("timeout receiving from channel") 396 } 397 398 require.NoError(harness.DestroyTask(task.ID, true)) 399 } 400 401 func TestExecDriver_Start_Wait_AllocDir(t *testing.T) { 402 t.Parallel() 403 require := require.New(t) 404 ctestutils.ExecCompatible(t) 405 406 d := NewExecDriver(testlog.HCLogger(t)) 407 harness := dtestutil.NewDriverHarness(t, d) 408 task := &drivers.TaskConfig{ 409 ID: uuid.Generate(), 410 Name: "sleep", 411 Resources: testResources, 412 } 413 cleanup := harness.MkAllocDir(task, false) 414 defer cleanup() 415 416 exp := []byte{'w', 'i', 'n'} 417 file := "output.txt" 418 tc := &TaskConfig{ 419 Command: "/bin/bash", 420 Args: []string{ 421 "-c", 422 fmt.Sprintf(`sleep 1; echo -n %s > /alloc/%s`, string(exp), file), 423 }, 424 } 425 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 426 427 handle, _, err := harness.StartTask(task) 428 require.NoError(err) 429 require.NotNil(handle) 430 431 // Task should terminate quickly 432 waitCh, err := harness.WaitTask(context.Background(), task.ID) 433 require.NoError(err) 434 select { 435 case res := <-waitCh: 436 require.True(res.Successful(), "task should have exited successfully: %v", res) 437 case <-time.After(time.Duration(testutil.TestMultiplier()*5) * time.Second): 438 require.Fail("timeout waiting for task") 439 } 440 441 // Check that data was written to the shared alloc directory. 442 outputFile := filepath.Join(task.TaskDir().SharedAllocDir, file) 443 act, err := ioutil.ReadFile(outputFile) 444 require.NoError(err) 445 require.Exactly(exp, act) 446 447 require.NoError(harness.DestroyTask(task.ID, true)) 448 } 449 450 func TestExecDriver_User(t *testing.T) { 451 t.Parallel() 452 require := require.New(t) 453 ctestutils.ExecCompatible(t) 454 455 d := NewExecDriver(testlog.HCLogger(t)) 456 harness := dtestutil.NewDriverHarness(t, d) 457 task := &drivers.TaskConfig{ 458 ID: uuid.Generate(), 459 Name: "sleep", 460 User: "alice", 461 Resources: testResources, 462 } 463 cleanup := harness.MkAllocDir(task, false) 464 defer cleanup() 465 466 tc := &TaskConfig{ 467 Command: "/bin/sleep", 468 Args: []string{"100"}, 469 } 470 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 471 472 handle, _, err := harness.StartTask(task) 473 require.Error(err) 474 require.Nil(handle) 475 476 msg := "user alice" 477 if !strings.Contains(err.Error(), msg) { 478 t.Fatalf("Expecting '%v' in '%v'", msg, err) 479 } 480 } 481 482 // TestExecDriver_HandlerExec ensures the exec driver's handle properly 483 // executes commands inside the container. 484 func TestExecDriver_HandlerExec(t *testing.T) { 485 t.Parallel() 486 require := require.New(t) 487 ctestutils.ExecCompatible(t) 488 489 d := NewExecDriver(testlog.HCLogger(t)) 490 harness := dtestutil.NewDriverHarness(t, d) 491 task := &drivers.TaskConfig{ 492 ID: uuid.Generate(), 493 Name: "sleep", 494 Resources: testResources, 495 } 496 cleanup := harness.MkAllocDir(task, false) 497 defer cleanup() 498 499 tc := &TaskConfig{ 500 Command: "/bin/sleep", 501 Args: []string{"9000"}, 502 } 503 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 504 505 handle, _, err := harness.StartTask(task) 506 require.NoError(err) 507 require.NotNil(handle) 508 509 // Exec a command that should work and dump the environment 510 // TODO: enable section when exec env is fully loaded 511 /*res, err := harness.ExecTask(task.ID, []string{"/bin/sh", "-c", "env | grep ^NOMAD"}, time.Second) 512 require.NoError(err) 513 require.True(res.ExitResult.Successful()) 514 515 // Assert exec'd commands are run in a task-like environment 516 scriptEnv := make(map[string]string) 517 for _, line := range strings.Split(string(res.Stdout), "\n") { 518 if line == "" { 519 continue 520 } 521 parts := strings.SplitN(string(line), "=", 2) 522 if len(parts) != 2 { 523 t.Fatalf("Invalid env var: %q", line) 524 } 525 scriptEnv[parts[0]] = parts[1] 526 } 527 if v, ok := scriptEnv["NOMAD_SECRETS_DIR"]; !ok || v != "/secrets" { 528 t.Errorf("Expected NOMAD_SECRETS_DIR=/secrets but found=%t value=%q", ok, v) 529 }*/ 530 531 // Assert cgroup membership 532 res, err := harness.ExecTask(task.ID, []string{"/bin/cat", "/proc/self/cgroup"}, time.Second) 533 require.NoError(err) 534 require.True(res.ExitResult.Successful()) 535 found := false 536 for _, line := range strings.Split(string(res.Stdout), "\n") { 537 // Every cgroup entry should be /nomad/$ALLOC_ID 538 if line == "" { 539 continue 540 } 541 // Skip rdma subsystem; rdma was added in most recent kernels and libcontainer/docker 542 // don't isolate it by default. 543 if strings.Contains(line, ":rdma:") { 544 continue 545 } 546 if !strings.Contains(line, ":/nomad/") { 547 t.Errorf("Not a member of the alloc's cgroup: expected=...:/nomad/... -- found=%q", line) 548 continue 549 } 550 found = true 551 } 552 require.True(found, "exec'd command isn't in the task's cgroup") 553 554 // Exec a command that should fail 555 res, err = harness.ExecTask(task.ID, []string{"/usr/bin/stat", "lkjhdsaflkjshowaisxmcvnlia"}, time.Second) 556 require.NoError(err) 557 require.False(res.ExitResult.Successful()) 558 if expected := "No such file or directory"; !bytes.Contains(res.Stdout, []byte(expected)) { 559 t.Fatalf("expected output to contain %q but found: %q", expected, res.Stdout) 560 } 561 562 require.NoError(harness.DestroyTask(task.ID, true)) 563 } 564 565 func TestExecDriver_DevicesAndMounts(t *testing.T) { 566 t.Parallel() 567 require := require.New(t) 568 ctestutils.ExecCompatible(t) 569 570 tmpDir, err := ioutil.TempDir("", "exec_binds_mounts") 571 require.NoError(err) 572 defer os.RemoveAll(tmpDir) 573 574 err = ioutil.WriteFile(filepath.Join(tmpDir, "testfile"), []byte("from-host"), 600) 575 require.NoError(err) 576 577 d := NewExecDriver(testlog.HCLogger(t)) 578 harness := dtestutil.NewDriverHarness(t, d) 579 task := &drivers.TaskConfig{ 580 ID: uuid.Generate(), 581 Name: "test", 582 User: "root", // need permission to read mounts paths 583 Resources: testResources, 584 StdoutPath: filepath.Join(tmpDir, "task-stdout"), 585 StderrPath: filepath.Join(tmpDir, "task-stderr"), 586 Devices: []*drivers.DeviceConfig{ 587 { 588 TaskPath: "/dev/inserted-random", 589 HostPath: "/dev/random", 590 Permissions: "rw", 591 }, 592 }, 593 Mounts: []*drivers.MountConfig{ 594 { 595 TaskPath: "/tmp/task-path-rw", 596 HostPath: tmpDir, 597 Readonly: false, 598 }, 599 { 600 TaskPath: "/tmp/task-path-ro", 601 HostPath: tmpDir, 602 Readonly: true, 603 }, 604 }, 605 } 606 607 require.NoError(ioutil.WriteFile(task.StdoutPath, []byte{}, 660)) 608 require.NoError(ioutil.WriteFile(task.StderrPath, []byte{}, 660)) 609 610 tc := &TaskConfig{ 611 Command: "/bin/bash", 612 Args: []string{"-c", ` 613 export LANG=en.UTF-8 614 echo "mounted device /inserted-random: $(stat -c '%t:%T' /dev/inserted-random)" 615 echo "reading from ro path: $(cat /tmp/task-path-ro/testfile)" 616 echo "reading from rw path: $(cat /tmp/task-path-rw/testfile)" 617 touch /tmp/task-path-rw/testfile && echo 'overwriting file in rw succeeded' 618 touch /tmp/task-path-rw/testfile-from-rw && echo from-exec > /tmp/task-path-rw/testfile-from-rw && echo 'writing new file in rw succeeded' 619 touch /tmp/task-path-ro/testfile && echo 'overwriting file in ro succeeded' 620 touch /tmp/task-path-ro/testfile-from-ro && echo from-exec > /tmp/task-path-ro/testfile-from-ro && echo 'writing new file in ro succeeded' 621 exit 0 622 `}, 623 } 624 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 625 626 cleanup := harness.MkAllocDir(task, false) 627 defer cleanup() 628 629 handle, _, err := harness.StartTask(task) 630 require.NoError(err) 631 632 ch, err := harness.WaitTask(context.Background(), handle.Config.ID) 633 require.NoError(err) 634 result := <-ch 635 require.NoError(harness.DestroyTask(task.ID, true)) 636 637 stdout, err := ioutil.ReadFile(task.StdoutPath) 638 require.NoError(err) 639 require.Equal(`mounted device /inserted-random: 1:8 640 reading from ro path: from-host 641 reading from rw path: from-host 642 overwriting file in rw succeeded 643 writing new file in rw succeeded`, strings.TrimSpace(string(stdout))) 644 645 stderr, err := ioutil.ReadFile(task.StderrPath) 646 require.NoError(err) 647 require.Equal(`touch: cannot touch '/tmp/task-path-ro/testfile': Read-only file system 648 touch: cannot touch '/tmp/task-path-ro/testfile-from-ro': Read-only file system`, strings.TrimSpace(string(stderr))) 649 650 // testing exit code last so we can inspect output first 651 require.Zero(result.ExitCode) 652 653 fromRWContent, err := ioutil.ReadFile(filepath.Join(tmpDir, "testfile-from-rw")) 654 require.NoError(err) 655 require.Equal("from-exec", strings.TrimSpace(string(fromRWContent))) 656 } 657 658 func TestConfig_ParseAllHCL(t *testing.T) { 659 cfgStr := ` 660 config { 661 command = "/bin/bash" 662 args = ["-c", "echo hello"] 663 }` 664 665 expected := &TaskConfig{ 666 Command: "/bin/bash", 667 Args: []string{"-c", "echo hello"}, 668 } 669 670 var tc *TaskConfig 671 hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc) 672 673 require.EqualValues(t, expected, tc) 674 } 675 676 func TestExecDriver_NoPivotRoot(t *testing.T) { 677 t.Parallel() 678 require := require.New(t) 679 ctestutils.ExecCompatible(t) 680 681 d := NewExecDriver(testlog.HCLogger(t)) 682 harness := dtestutil.NewDriverHarness(t, d) 683 684 config := &Config{NoPivotRoot: true} 685 var data []byte 686 require.NoError(basePlug.MsgPackEncode(&data, config)) 687 bconfig := &basePlug.Config{PluginConfig: data} 688 require.NoError(harness.SetConfig(bconfig)) 689 690 task := &drivers.TaskConfig{ 691 ID: uuid.Generate(), 692 Name: "sleep", 693 Resources: testResources, 694 } 695 cleanup := harness.MkAllocDir(task, false) 696 defer cleanup() 697 698 tc := &TaskConfig{ 699 Command: "/bin/sleep", 700 Args: []string{"100"}, 701 } 702 require.NoError(task.EncodeConcreteDriverConfig(&tc)) 703 704 handle, _, err := harness.StartTask(task) 705 require.NoError(err) 706 require.NotNil(handle) 707 }