Skip to content

Commit e0b03f5

Browse files
Merge branch 'master' into test_delegation
2 parents b8e9958 + e84416f commit e0b03f5

File tree

14 files changed

+153
-39
lines changed

14 files changed

+153
-39
lines changed

.github/workflows/codeql.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232

3333
# Initializes the CodeQL tools for scanning.
3434
- name: Initialize CodeQL
35-
uses: github/codeql-action/init@v3
35+
uses: github/codeql-action/init@v4
3636
with:
3737
languages: ${{ matrix.language }}
3838
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -43,6 +43,6 @@ jobs:
4343
# queries: security-extended,security-and-quality
4444

4545
- name: Perform CodeQL Analysis
46-
uses: github/codeql-action/analyze@v3
46+
uses: github/codeql-action/analyze@v4
4747
with:
4848
category: "/language:${{matrix.language}}"

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,6 @@ ENV/
100100

101101
# mkdocs documentation
102102
/site
103+
104+
# AI helpers
105+
.aider*

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ init_lint:
1414
.PHONY: lint
1515
lint:
1616
pre-commit run -a --show-diff-on-failure --color=always
17-
if command -v pytype >/dev/null 2>&1; then pytype -k -j auto cardano_node_tests; fi
1817

1918

2019
# check if development environment is set up correctly

cardano_node_tests/cluster_management/cluster_getter.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
1-
"""Functionality for obtaining and setting up a cluster instance."""
1+
"""Functionality for obtaining and setting up a cluster instance for parallel test execution.
2+
3+
The `ClusterGetter` class is responsible for managing a pool of cluster instances and assigning them
4+
to tests running in parallel on different pytest workers. It ensures that tests get a suitable,
5+
properly configured, and healthy cluster instance to run on.
6+
7+
Coordination between workers is achieved through a system of status files created in a shared
8+
temporary directory. These files signal the state of each cluster instance (e.g., running,
9+
needs respin), which tests are running on which instance, and what resources are locked or in use.
10+
11+
The core logic is implemented in the `get_cluster_instance` method. It enters a loop where it
12+
evaluates the state of all available cluster instances against the requirements of the current test
13+
(e.g., resource needs, custom scripts, priority). It will wait and retry until a suitable instance
14+
is found and all conditions for starting the test are met. This includes handling cluster restarts
15+
(respins), resource allocation, and synchronization for tests that share expensive setups
16+
(marked tests).
17+
"""
218

319
import dataclasses
420
import logging

cardano_node_tests/cluster_management/cluster_management.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
1-
"""Module for exposing useful components of cluster management."""
1+
"""Module for exposing useful components of cluster management.
2+
3+
The cluster management system is designed to manage a pool of Cardano cluster instances for running
4+
tests in parallel using `pytest-xdist`. It coordinates access to these shared cluster instances
5+
by multiple test workers.
6+
7+
Key concepts:
8+
- **Pool of Instances**: Multiple cluster instances can be running concurrently. Each test worker
9+
requests a cluster instance to run a test on.
10+
- **Coordination via File System**: Workers communicate and coordinate through a system of status
11+
files created on a shared file system. These files act as locks and signals to indicate the
12+
state of cluster instances (e.g., which test is running, if a respin is needed, which
13+
resources are locked). The `status_files` module manages the creation and lookup of these
14+
files.
15+
- **Resource Management**: Tests can declare what resources they need. A resource can be, for
16+
example, a specific feature of a cluster that cannot be used by multiple tests at the same
17+
time. The `ClusterManager` handles locking of these resources so that only one test can use
18+
them at a time.
19+
- **Cluster Respin**: Some tests can modify the state of a cluster in a way that it cannot be
20+
used by subsequent tests. These tests can request a "respin" of the cluster instance, which
21+
re-initializes it to a clean state.
22+
- **`ClusterManager`**: This is the main class that test fixtures interact with. Its `get()`
23+
method is used to acquire a suitable cluster instance for a test, taking into account available
24+
instances, resource requirements, and scheduling priority.
25+
26+
This system allows for efficient parallel execution of tests that require a running Cardano
27+
cluster, by reusing cluster instances and managing contention for shared resources.
28+
"""
229

330
# flake8: noqa
431
from cardano_node_tests.cluster_management.common import CLUSTER_LOCK

cardano_node_tests/cluster_management/manager.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
"""Functionality for managing cluster instances."""
1+
"""High-level management of cluster instances.
2+
3+
This module provides the `ClusterManager` class, which is the main interface for tests to get a
4+
fully initialized cluster instance. The `ClusterManager` is responsible for selecting an available
5+
cluster instance that meets the test's resource requirements, preparing the `clusterlib` object,
6+
and performing cleanup actions after the test has finished.
7+
8+
The `ClusterManager` is instantiated by the `cluster_manager` fixture for each test worker and is
9+
used by the `cluster` fixture to get a cluster instance for a test.
10+
"""
211

312
import contextlib
413
import dataclasses

cardano_node_tests/cluster_management/resources_management.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
"""Functionality for getting a cluster instance that has required resources available."""
1+
"""Functionality for selecting a cluster instance that has required resources available.
2+
3+
Resources can be requested by name (string).
4+
5+
It is also possible to use filters. A filter is a class that gets a list of all unavailable
6+
resources and returns a list of resources that should be used. An example is `OneOf`, which returns
7+
one usable resource from a given list of resources. The unavailable resources passed to the filter
8+
include resources that are unavailable because they are locked, and also resources that were
9+
already selected by preceding filters in the same request.
10+
11+
It is possible to use multiple `OneOf` filters in a single request. For example, using `OneOf`
12+
filter with the same set of resources twice will result in selecting two different resources from
13+
that set.
14+
"""
215

316
import random
417
import typing as tp

cardano_node_tests/cluster_management/status_files.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
"""Cluster instance status files.
2+
3+
Status files are used for communication and synchronization between pytest workers.
4+
5+
All status files are created in the single temp directory shared by all workers
6+
(the directory returned by `temptools.get_pytest_root_tmp()`). This allows all
7+
workers to see status files created by other workers.
8+
9+
Common components of status file names:
10+
* `_@@<resource_name>@@_`: resource name
11+
* `_%%<mark>%%_`: test mark
12+
* `_<worker_id>`: pytest worker ID
13+
"""
14+
115
import pathlib as pl
216
import re
317
import typing as tp

cardano_node_tests/tests/tests_conway/test_drep.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -845,9 +845,11 @@ def _deregister():
845845

846846
stake_addr_info = cluster.g_query.get_stake_addr_info(pool_user_rewards.stake.address)
847847

848-
assert stake_addr_info.vote_delegation == governance_utils.get_drep_cred_name(
849-
drep_id=drep1.drep_id
850-
), "Votes are NOT delegated to the correct DRep 1 placed at last of certificates list."
848+
assert governance_utils.get_drep_cred_name_from_addr_info(
849+
addr_info=stake_addr_info
850+
) == governance_utils.get_drep_cred_name(drep_id=drep1.drep_id), (
851+
"Votes are NOT delegated to the correct DRep 1 placed at last of certificates list."
852+
)
851853
reqc.cip087.success()
852854

853855
@allure.link(helpers.get_vcs_link())
@@ -1163,9 +1165,11 @@ def _deregister():
11631165
f"Stake address is NOT registered: {pool_user_rewards.stake.address}"
11641166
)
11651167
reqc.cli035.start(url=helpers.get_vcs_link())
1166-
assert stake_addr_info.vote_delegation == governance_utils.get_drep_cred_name(
1167-
drep_id=drep_id
1168-
), "Votes are NOT delegated to the correct DRep"
1168+
assert governance_utils.get_drep_cred_name_from_addr_info(
1169+
addr_info=stake_addr_info
1170+
) == governance_utils.get_drep_cred_name(drep_id=drep_id), (
1171+
"Votes are NOT delegated to the correct DRep"
1172+
)
11691173
reqc.cli035.success()
11701174

11711175
out_utxos = cluster.g_query.get_utxo(tx_raw_output=tx_output)
@@ -1359,9 +1363,11 @@ def _deregister():
13591363
stake_addr_info = cluster.g_query.get_stake_addr_info(pool_user_wpr.stake.address)
13601364
assert stake_addr_info.delegation, f"Stake address was not delegated yet: {stake_addr_info}"
13611365
assert pool_id == stake_addr_info.delegation, "Stake address delegated to wrong pool"
1362-
assert stake_addr_info.vote_delegation == governance_utils.get_drep_cred_name(
1363-
drep_id=drep_id
1364-
), "Votes are NOT delegated to the correct DRep"
1366+
assert governance_utils.get_drep_cred_name_from_addr_info(
1367+
addr_info=stake_addr_info
1368+
) == governance_utils.get_drep_cred_name(drep_id=drep_id), (
1369+
"Votes are NOT delegated to the correct DRep"
1370+
)
13651371

13661372
# Check the expected balance
13671373
out_utxos = cluster.g_query.get_utxo(tx_raw_output=tx_output)
@@ -1518,9 +1524,11 @@ def _deregister():
15181524
stake_addr_info_deleg1 = cluster.g_query.get_stake_addr_info(
15191525
pool_user_rewards.stake.address
15201526
)
1521-
assert stake_addr_info_deleg1.vote_delegation == governance_utils.get_drep_cred_name(
1522-
drep_id=drep1.drep_id
1523-
), "Votes are NOT delegated to the correct DRep 1"
1527+
assert governance_utils.get_drep_cred_name_from_addr_info(
1528+
addr_info=stake_addr_info_deleg1
1529+
) == governance_utils.get_drep_cred_name(drep_id=drep1.drep_id), (
1530+
"Votes are NOT delegated to the correct DRep 1"
1531+
)
15241532

15251533
# Change delegation to second DRep
15261534

@@ -1549,9 +1557,11 @@ def _deregister():
15491557
stake_addr_info_deleg2 = cluster.g_query.get_stake_addr_info(
15501558
pool_user_rewards.stake.address
15511559
)
1552-
assert stake_addr_info_deleg2.vote_delegation == governance_utils.get_drep_cred_name(
1553-
drep_id=drep2.drep_id
1554-
), "Votes are NOT changed to the correct DRep 2"
1560+
assert governance_utils.get_drep_cred_name_from_addr_info(
1561+
addr_info=stake_addr_info_deleg2
1562+
) == governance_utils.get_drep_cred_name(drep_id=drep2.drep_id), (
1563+
"Votes are NOT changed to the correct DRep 2"
1564+
)
15551565
reqc.cip086.success()
15561566

15571567
# Retire the first DRep
@@ -1588,9 +1598,11 @@ def _deregister():
15881598
else:
15891599
if not stake_addr_info_ret.vote_delegation:
15901600
issues.ledger_4772.finish_test()
1591-
assert stake_addr_info_ret.vote_delegation == governance_utils.get_drep_cred_name(
1592-
drep_id=drep2.drep_id
1593-
), "Votes are no longer delegated to DRep 2!"
1601+
assert governance_utils.get_drep_cred_name_from_addr_info(
1602+
addr_info=stake_addr_info_ret
1603+
) == governance_utils.get_drep_cred_name(drep_id=drep2.drep_id), (
1604+
"Votes are no longer delegated to DRep 2!"
1605+
)
15941606
reqc.int002.success()
15951607

15961608

@@ -1707,9 +1719,11 @@ def _delegate_addr(
17071719
assert stake_addr_info.address, (
17081720
f"Stake address is NOT registered: {pool_user.stake.address}"
17091721
)
1710-
assert stake_addr_info.vote_delegation == governance_utils.get_drep_cred_name(
1711-
drep_id=drep_reg.drep_id
1712-
), "Votes are NOT delegated to the correct DRep"
1722+
assert governance_utils.get_drep_cred_name_from_addr_info(
1723+
addr_info=stake_addr_info
1724+
) == governance_utils.get_drep_cred_name(drep_id=drep_reg.drep_id), (
1725+
"Votes are NOT delegated to the correct DRep"
1726+
)
17131727

17141728
out_utxos = cluster.g_query.get_utxo(tx_raw_output=tx_output)
17151729
assert (

cardano_node_tests/utils/gh_issue.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ def get_state(self) -> str | None:
6161
cached_state = self.issue_cache.get(identifier)
6262

6363
if cached_state is None:
64-
assert self.github # for pytype
6564
try:
6665
cached_state = self.github.get_repo(self.repo).get_issue(self.number).state.lower()
6766
except github.UnknownObjectException:

0 commit comments

Comments
 (0)