|
29 | 29 | import socket |
30 | 30 | import time |
31 | 31 | import signal |
| 32 | +from threading import Thread |
| 33 | +import re |
32 | 34 | from pathlib import Path |
33 | 35 | from typing import NoReturn, Tuple, IO, TypeAlias |
34 | 36 | from collections.abc import Callable |
@@ -2443,6 +2445,196 @@ def privleapd_config_reload_with_kick_test(bogus: str) -> bool: |
2443 | 2445 | return assert_success |
2444 | 2446 |
|
2445 | 2447 |
|
| 2448 | +def privleapd_multithreading_test_monitor() -> None: |
| 2449 | + """ |
| 2450 | + Monitors the stderr from privleapd for unexpected lines, printing errors |
| 2451 | + and setting a flag if an unexpected line is detected. |
| 2452 | + """ |
| 2453 | + |
| 2454 | + assert PlTestGlobal.privleapd_proc is not None |
| 2455 | + assert PlTestGlobal.privleapd_proc.stderr is not None |
| 2456 | + linebuf: str = "" |
| 2457 | + while True: |
| 2458 | + if PlTestGlobal.multithreading_test_monitor_stop: |
| 2459 | + break |
| 2460 | + while "\n" in linebuf: |
| 2461 | + linebuf_parts: list[str] = linebuf.split("\n", maxsplit=1) |
| 2462 | + linebuf = linebuf_parts[1] |
| 2463 | + line: str = linebuf_parts[0] |
| 2464 | + line = re.sub( |
| 2465 | + "privleaptest(one|two|three|four|five|six|seven|eight|" |
| 2466 | + + "nine|ten)", |
| 2467 | + "XXX_USERNAME_XXX", |
| 2468 | + line |
| 2469 | + ) |
| 2470 | + if line not in PlTestData.multithreading_test_set: |
| 2471 | + logging.error( |
| 2472 | + "Unexpected stderr line from privleapd: '%s'", line |
| 2473 | + ) |
| 2474 | + PlTestGlobal.multithreading_test_unexpected_stderr = True |
| 2475 | + else: |
| 2476 | + logging.info("Valid line '%s' received from privleapd", line) |
| 2477 | + |
| 2478 | + try: |
| 2479 | + linebuf += PlTestGlobal.privleapd_proc.stderr.read() |
| 2480 | + except Exception: |
| 2481 | + pass |
| 2482 | + time.sleep(0.1) |
| 2483 | + |
| 2484 | + |
| 2485 | +# pylint: disable=consider-using-with |
| 2486 | +# Rationale: |
| 2487 | +# consider-using-with: Not suitable for the parallel process running |
| 2488 | +# mechanism being used here. |
| 2489 | +def privleapd_multithreading_test_worker( |
| 2490 | + mode: str, user_name: str |
| 2491 | +) -> subprocess.Popen[bytes]: |
| 2492 | + """ |
| 2493 | + Calls leapctl or leaprun to do an action asynchronously. |
| 2494 | + """ |
| 2495 | + |
| 2496 | + return_proc: subprocess.Popen[bytes] |
| 2497 | + match mode: |
| 2498 | + case "create": |
| 2499 | + return_proc = subprocess.Popen( |
| 2500 | + ["leapctl", "--create", user_name], |
| 2501 | + stdin=subprocess.PIPE, |
| 2502 | + stdout=subprocess.PIPE, |
| 2503 | + ) |
| 2504 | + case "run": |
| 2505 | + return_proc = subprocess.Popen( |
| 2506 | + ["sudo", "-u", user_name, "leaprun", "test-act-anyone"], |
| 2507 | + stdin=subprocess.PIPE, |
| 2508 | + stdout=subprocess.PIPE, |
| 2509 | + ) |
| 2510 | + case "destroy": |
| 2511 | + return_proc = subprocess.Popen( |
| 2512 | + ["leapctl", "--destroy", user_name], |
| 2513 | + stdin=subprocess.PIPE, |
| 2514 | + stdout=subprocess.PIPE, |
| 2515 | + ) |
| 2516 | + return return_proc |
| 2517 | + |
| 2518 | + |
| 2519 | +# pylint: disable=too-many-branches |
| 2520 | +# Rationale: |
| 2521 | +# too-many-branches: Using less branches isn't practical, splitting this up |
| 2522 | +# would only make it harder to read. |
| 2523 | +def privleapd_multithreading_test(bogus: str) -> bool: |
| 2524 | + """ |
| 2525 | + Exercises privleapd's multithreading code. |
| 2526 | + """ |
| 2527 | + |
| 2528 | + if bogus != "": |
| 2529 | + return False |
| 2530 | + discard_privleapd_stderr() |
| 2531 | + |
| 2532 | + proc_list: list[subprocess.Popen[bytes]] = [] |
| 2533 | + first_user_name_block = [ |
| 2534 | + "privleaptestone", |
| 2535 | + "privleaptesttwo", |
| 2536 | + "privleaptestthree", |
| 2537 | + "privleaptestfour", |
| 2538 | + "privleaptestfive", |
| 2539 | + ] |
| 2540 | + second_user_name_block = [ |
| 2541 | + "privleaptestsix", |
| 2542 | + "privleaptestseven", |
| 2543 | + "privleaptesteight", |
| 2544 | + "privleaptestnine", |
| 2545 | + "privleaptestten", |
| 2546 | + ] |
| 2547 | + monitor_thread: Thread = Thread( |
| 2548 | + target=privleapd_multithreading_test_monitor, |
| 2549 | + ) |
| 2550 | + monitor_thread.start() |
| 2551 | + |
| 2552 | + for user_name in first_user_name_block: |
| 2553 | + proc_list.append( |
| 2554 | + privleapd_multithreading_test_worker("create", user_name) |
| 2555 | + ) |
| 2556 | + for proc in proc_list: |
| 2557 | + proc.communicate() |
| 2558 | + proc_list.clear() |
| 2559 | + |
| 2560 | + for loop_idx in range(0, 20): |
| 2561 | + for first_block_user_name, second_block_user_name in zip( |
| 2562 | + first_user_name_block, second_user_name_block |
| 2563 | + ): |
| 2564 | + proc_list.append( |
| 2565 | + privleapd_multithreading_test_worker( |
| 2566 | + "run", first_block_user_name |
| 2567 | + ) |
| 2568 | + ) |
| 2569 | + proc_list.append( |
| 2570 | + privleapd_multithreading_test_worker( |
| 2571 | + "create", second_block_user_name |
| 2572 | + ) |
| 2573 | + ) |
| 2574 | + for proc in proc_list: |
| 2575 | + proc.communicate() |
| 2576 | + proc_list.clear() |
| 2577 | + logging.info( |
| 2578 | + f"Multithreading test, part 1 of iteration {loop_idx + 1} done" |
| 2579 | + ) |
| 2580 | + |
| 2581 | + for first_block_user_name, second_block_user_name in zip( |
| 2582 | + first_user_name_block, second_user_name_block |
| 2583 | + ): |
| 2584 | + proc_list.append( |
| 2585 | + privleapd_multithreading_test_worker( |
| 2586 | + "destroy", first_block_user_name |
| 2587 | + ) |
| 2588 | + ) |
| 2589 | + proc_list.append( |
| 2590 | + privleapd_multithreading_test_worker( |
| 2591 | + "run", second_block_user_name |
| 2592 | + ) |
| 2593 | + ) |
| 2594 | + for proc in proc_list: |
| 2595 | + proc.communicate() |
| 2596 | + proc_list.clear() |
| 2597 | + logging.info( |
| 2598 | + f"Multithreading test, part 2 of iteration {loop_idx + 1} done" |
| 2599 | + ) |
| 2600 | + |
| 2601 | + for first_block_user_name, second_block_user_name in zip( |
| 2602 | + first_user_name_block, second_user_name_block |
| 2603 | + ): |
| 2604 | + proc_list.append( |
| 2605 | + privleapd_multithreading_test_worker( |
| 2606 | + "create", first_block_user_name |
| 2607 | + ) |
| 2608 | + ) |
| 2609 | + proc_list.append( |
| 2610 | + privleapd_multithreading_test_worker( |
| 2611 | + "destroy", second_block_user_name |
| 2612 | + ) |
| 2613 | + ) |
| 2614 | + for proc in proc_list: |
| 2615 | + proc.communicate() |
| 2616 | + proc_list.clear() |
| 2617 | + logging.info( |
| 2618 | + f"Multithreading test, part 3 of iteration {loop_idx + 1} done" |
| 2619 | + ) |
| 2620 | + |
| 2621 | + for user_name in first_user_name_block: |
| 2622 | + proc_list.append( |
| 2623 | + privleapd_multithreading_test_worker("destroy", user_name) |
| 2624 | + ) |
| 2625 | + for proc in proc_list: |
| 2626 | + proc.communicate() |
| 2627 | + proc_list.clear() |
| 2628 | + |
| 2629 | + time.sleep(0.5) |
| 2630 | + PlTestGlobal.multithreading_test_monitor_stop = True |
| 2631 | + time.sleep(0.5) |
| 2632 | + |
| 2633 | + if PlTestGlobal.multithreading_test_unexpected_stderr: |
| 2634 | + return False |
| 2635 | + return True |
| 2636 | + |
| 2637 | + |
2446 | 2638 | def privleapd_assert_command( |
2447 | 2639 | command_data: list[str], |
2448 | 2640 | exit_code: int, |
@@ -3318,6 +3510,12 @@ def run_privleapd_tests() -> None: |
3318 | 3510 | "/test-act-interrupt", |
3319 | 3511 | "Remove flag file left by test-act-interrupt", |
3320 | 3512 | ) |
| 3513 | + # --- |
| 3514 | + privleapd_assert_function( |
| 3515 | + privleapd_multithreading_test, |
| 3516 | + "", |
| 3517 | + "Test privleapd multithreading code", |
| 3518 | + ) |
3321 | 3519 | privleapd_assert_command( |
3322 | 3520 | ["leapctl", "--create", "privleaptestone"], |
3323 | 3521 | exit_code=0, |
@@ -3395,9 +3593,21 @@ def main() -> NoReturn: |
3395 | 3593 | print_test_header() |
3396 | 3594 | ensure_running_as_root() |
3397 | 3595 | stop_privleapd_service() |
3398 | | - setup_test_account("privleaptestone", Path("/home/privleaptestone")) |
3399 | | - setup_test_account("privleaptesttwo", Path("/home/privleaptesttwo")) |
3400 | | - setup_test_account("privleaptestthree", Path("/home/privleaptestthree")) |
| 3596 | + for setup_account_name in [ |
| 3597 | + "privleaptestone", |
| 3598 | + "privleaptesttwo", |
| 3599 | + "privleaptestthree", |
| 3600 | + "privleaptestfour", |
| 3601 | + "privleaptestfive", |
| 3602 | + "privleaptestsix", |
| 3603 | + "privleaptestseven", |
| 3604 | + "privleaptesteight", |
| 3605 | + "privleaptestnine", |
| 3606 | + "privleaptestten", |
| 3607 | + ]: |
| 3608 | + setup_test_account( |
| 3609 | + setup_account_name, Path(f"/home/{setup_account_name}") |
| 3610 | + ) |
3401 | 3611 | displace_old_privleap_config() |
3402 | 3612 | write_privleap_test_config() |
3403 | 3613 |
|
|
0 commit comments