|
21 | 21 | from functools import partial
|
22 | 22 | from itertools import product
|
23 | 23 | from shutil import copyfile
|
| 24 | +from time import sleep |
24 | 25 | from traceback import format_tb
|
25 | 26 | from typing import Any, Dict, List, Optional, Union
|
26 | 27 |
|
@@ -366,6 +367,70 @@ def _setup_custom_logger(log_file):
|
366 | 367 | logger.addHandler(file_handler)
|
367 | 368 |
|
368 | 369 |
|
| 370 | +class SharedClusterDetectionTimeoutError(Exception): |
| 371 | + """Custom exception for shared cluster detection timeout.""" |
| 372 | + |
| 373 | + pass |
| 374 | + |
| 375 | + |
| 376 | +@pytest.fixture(scope="module") |
| 377 | +@pytest.mark.usefixtures("setup_credentials") |
| 378 | +def shared_clusters_factory(request): |
| 379 | + """ |
| 380 | + Define a fixture to manage the creation and destruction of module shared clusters. |
| 381 | +
|
| 382 | + The configs used to create clusters are dumped to output_dir/clusters_configs/{test_name}.config |
| 383 | + """ |
| 384 | + factory = ClustersFactory(delete_logs_on_success=request.config.getoption("delete_logs_on_success")) |
| 385 | + |
| 386 | + if not hasattr(request.module, "shared_existing_cluster"): |
| 387 | + request.module.shared_existing_cluster = None |
| 388 | + request.module.is_cluster_started_to_create = False |
| 389 | + |
| 390 | + def _cluster_factory(cluster_config, region, upper_case_cluster_name=False, custom_cli_credentials=None, **kwargs): |
| 391 | + if request.module.is_cluster_started_to_create: |
| 392 | + for retry in range(40): |
| 393 | + if request.module.shared_existing_cluster: |
| 394 | + logging.info(f"Shared cluster {request.module.shared_existing_cluster.name} detected.") |
| 395 | + return request.module.shared_existing_cluster |
| 396 | + else: |
| 397 | + logging.info(f"Shared cluster not detected yet. Retrying... ({retry + 1}/40)") |
| 398 | + sleep(60) |
| 399 | + raise SharedClusterDetectionTimeoutError( |
| 400 | + "Timeout: Failed to detect the shared cluster within the allowed retries." |
| 401 | + ) |
| 402 | + |
| 403 | + request.module.is_cluster_started_to_create = True |
| 404 | + cluster_config = _write_config_to_outdir(request, cluster_config, "clusters_configs") |
| 405 | + cluster = Cluster( |
| 406 | + name=( |
| 407 | + request.config.getoption("cluster") |
| 408 | + if request.config.getoption("cluster") |
| 409 | + else "integ-tests-{0}{1}{2}".format( |
| 410 | + random_alphanumeric().upper() if upper_case_cluster_name else random_alphanumeric(), |
| 411 | + "-" if request.config.getoption("stackname_suffix") else "", |
| 412 | + request.config.getoption("stackname_suffix"), |
| 413 | + ) |
| 414 | + ), |
| 415 | + config_file=cluster_config, |
| 416 | + ssh_key=request.config.getoption("key_path"), |
| 417 | + region=region, |
| 418 | + custom_cli_credentials=custom_cli_credentials, |
| 419 | + ) |
| 420 | + if not request.config.getoption("cluster"): |
| 421 | + cluster.creation_response = factory.create_cluster(cluster, **kwargs) |
| 422 | + request.module.shared_existing_cluster = cluster |
| 423 | + return cluster |
| 424 | + |
| 425 | + yield _cluster_factory |
| 426 | + if not request.config.getoption("no_delete"): |
| 427 | + try: |
| 428 | + test_passed = request.node.rep_call.passed |
| 429 | + except AttributeError: |
| 430 | + test_passed = False |
| 431 | + factory.destroy_all_clusters(test_passed=test_passed) |
| 432 | + |
| 433 | + |
369 | 434 | @pytest.fixture(scope="class")
|
370 | 435 | @pytest.mark.usefixtures("setup_credentials")
|
371 | 436 | def clusters_factory(request, region):
|
@@ -509,9 +574,21 @@ def _write_config_to_outdir(request, config, config_dir):
|
509 | 574 | out_dir = request.config.getoption("output_dir")
|
510 | 575 |
|
511 | 576 | # Sanitize config file name to make it Windows compatible
|
512 |
| - # request.node.nodeid example: |
| 577 | + # class scope request.node.nodeid example: |
513 | 578 | # 'dcv/test_dcv.py::test_dcv_configuration[eu-west-1-c5.xlarge-centos7-slurm-8443-0.0.0.0/0-/shared]'
|
514 |
| - test_file, test_name = request.node.nodeid.split("::", 1) |
| 579 | + # module scope request.node.nodeid example: |
| 580 | + # 'performance_tests/test_starccm_and_openfoam.py' |
| 581 | + # TODO: Find a better way to name module_scope_test |
| 582 | + logging.info(f"request.node.nodeid: {request.node.nodeid}") |
| 583 | + nodeid_parts = request.node.nodeid.split("::") |
| 584 | + if len(nodeid_parts) == 2: |
| 585 | + test_file, test_name = nodeid_parts |
| 586 | + elif len(nodeid_parts) == 1: |
| 587 | + test_file = nodeid_parts[0] |
| 588 | + test_name = "module_scope_test" |
| 589 | + else: |
| 590 | + raise ValueError(f"Unexpected nodeid format: {request.node.nodeid}") |
| 591 | + |
515 | 592 | config_file_name = "{0}-{1}".format(test_file, test_name.replace("/", "_"))
|
516 | 593 |
|
517 | 594 | os.makedirs(
|
|
0 commit comments