diff --git a/core/updates.py b/core/updates.py index dba476e7..9a93e4eb 100644 --- a/core/updates.py +++ b/core/updates.py @@ -23,7 +23,9 @@ from core.node_config import NodeConfig from core.ima.schain import update_predeployed_ima - +from core.schains.config.file_manager import ConfigFileManager +from core.schains.cleaner import get_schains_on_node +from tools.docker_utils import DockerUtils logger = logging.getLogger(__name__) @@ -56,3 +58,22 @@ def update_node_config_file(skale: Skale, node_config: NodeConfig) -> None: node_config.ip = ip if node_config.name != name: node_config.name = name + + +def update_unsafe_for_schains( + skale: Skale, + node_config: NodeConfig, + dutils: DockerUtils +) -> list[str]: + schains_on_node = get_schains_on_node(dutils=dutils) + unsafe_chains = [] + for schain_name in schains_on_node: + cfm = ConfigFileManager(schain_name=schain_name) + if skale.node_rotation.is_rotation_active(schain_name): + logger.info('Rotation is in progress for %s', schain_name) + unsafe_chains.append(schain_name) + # To handle the gap between SM finish ts and skaled exit time + elif cfm.skaled_config_exists() and not cfm.skaled_config_synced_with_upstream(): + logger.info('Skaled config is not synced with upstream for %s', schain_name) + unsafe_chains.append(schain_name) + return unsafe_chains diff --git a/tests/routes/node_test.py b/tests/routes/node_test.py index 61e351d6..c0bc291a 100644 --- a/tests/routes/node_test.py +++ b/tests/routes/node_test.py @@ -14,11 +14,13 @@ from core.node import Node, NodeStatus from core.node_config import NodeConfig -from tests.utils import get_bp_data, post_bp_data +from core.schains.config.file_manager import ConfigFileManager from tools.configs.tg import TG_API_KEY, TG_CHAT_ID from web.routes.node import node_bp from web.helper import get_api_url +from tests.utils import get_bp_data, post_bp_data + CURRENT_TIMESTAMP = 1594903080 CURRENT_DATETIME = datetime.datetime.utcfromtimestamp(CURRENT_TIMESTAMP) @@ -27,7 +29,7 @@ @pytest.fixture -def skale_bp(skale, dutils): +def skale_bp(skale, node_config, dutils): app = Flask(__name__) app.register_blueprint(node_bp) @@ -40,32 +42,14 @@ def handler(sender, **kwargs): yield app.test_client() -@pytest.fixture -def node_contracts(skale): - ip, public_ip, port, name = generate_random_node_data() - skale.manager.create_node(ip, port, name, - domain_name=DEFAULT_DOMAIN_NAME, wait_for=True) - node_id = skale.nodes.node_name_to_index(name) - yield node_id - skale.nodes.init_exit(node_id) - skale.manager.node_exit(node_id, wait_for=True) - - -@pytest.fixture -def node_config(node_contracts): - config = NodeConfig() - config.id = node_contracts - return config - - -def test_node_info(skale_bp, skale, node_config): +def test_node_info(skale_bp, skale, node_config, node_wallets): data = get_bp_data(skale_bp, get_api_url(BLUEPRINT_NAME, 'info')) status = NodeStatus.ACTIVE.value assert data['status'] == 'ok' node_info = data['payload']['node_info'] assert node_info['id'] == node_config.id assert node_info['status'] == status - assert to_checksum_address(node_info['owner']) == skale.wallet.address + assert to_checksum_address(node_info['owner']) == node_wallets[0].address def register_mock(self, ip, public_ip, port, name, domain_name, gas_limit=None, @@ -272,3 +256,32 @@ def test_exit_maintenance(skale_bp, node_config_in_maintenance): ) assert data['status'] == 'error' data['payload'] == {} + + +def test_update_safe(skale, schain_on_contracts, schain_config, upstreams, skale_bp): + data = get_bp_data( + skale_bp, + get_api_url(BLUEPRINT_NAME, 'update-safe'), + ) + assert data['status'] == 'ok' + assert data['payload'] == {'update_safe': True, 'unsafe_chains': []} + + with mock.patch('web.helper.init_skale', return_value=skale): + with mock.patch.object(skale.node_rotation, 'is_rotation_active', return_value=False): + skale.node_rotation.is_rotation_active = mock.Mock(return_value=True) + data = get_bp_data( + skale_bp, + get_api_url(BLUEPRINT_NAME, 'update-safe'), + ) + assert data['payload'] == {'update_safe': False, 'unsafe_chains': [schain_on_contracts]} + + cfm = ConfigFileManager(schain_on_contracts) + + cfm.save_skaled_config({}) + + data = get_bp_data( + skale_bp, + get_api_url(BLUEPRINT_NAME, 'update-safe'), + ) + + assert data['payload'] == {'update_safe': False, 'unsafe_chains': [schain_on_contracts]} diff --git a/tests/schains/monitor/main_test.py b/tests/schains/monitor/main_test.py index 865fe081..f637b4c9 100644 --- a/tests/schains/monitor/main_test.py +++ b/tests/schains/monitor/main_test.py @@ -83,13 +83,14 @@ def test_run_monitor_for_schain_left( ): schain_not_exists = 'not-on-node' upsert_schain_record(schain_not_exists) - with mock.patch('core.schains.monitor.main.keep_tasks_running') as keep_tasks_running_mock: - run_monitor_for_schain( - skale, - skale_ima, - node_config, - get_schain_struct(schain_name=schain_db), - dutils=dutils, - once=True - ) - keep_tasks_running_mock.assert_not_called() + with mock.patch('core.schains.monitor.main.is_node_part_of_chain', return_value=False): + with mock.patch('core.schains.monitor.main.keep_tasks_running') as keep_tasks_running_mock: + run_monitor_for_schain( + skale, + skale_ima, + node_config, + get_schain_struct(schain_name=schain_not_exists), + dutils=dutils, + once=True + ) + keep_tasks_running_mock.assert_not_called() diff --git a/web/routes/node.py b/web/routes/node.py index 37360338..0d2a5e88 100644 --- a/web/routes/node.py +++ b/web/routes/node.py @@ -29,7 +29,7 @@ from core.node import get_meta_info, get_node_hardware_info, get_btrfs_info, get_abi_hash from core.node import check_validator_nodes - +from core.updates import update_unsafe_for_schains from tools.configs.web3 import ABI_FILEPATH, ENDPOINT, UNTRUSTED_PROVIDERS from tools.configs.ima import MAINNET_IMA_ABI_FILEPATH @@ -266,3 +266,12 @@ def ima_abi(): logger.debug(request) abi_hash = get_abi_hash(MAINNET_IMA_ABI_FILEPATH) return construct_ok_response(data=abi_hash) + + +@node_bp.route(get_api_url(BLUEPRINT_NAME, 'update-safe'), methods=['GET']) +@g_skale +def update_safe(): + logger.debug(request) + unsafe_chains = update_unsafe_for_schains(g.skale, g.config, g.docker_utils) + safe = len(unsafe_chains) == 0 + return construct_ok_response(data={'update_safe': safe, 'unsafe_chains': unsafe_chains}) diff --git a/web/routes/schains.py b/web/routes/schains.py index 060a83ff..58963321 100644 --- a/web/routes/schains.py +++ b/web/routes/schains.py @@ -85,7 +85,6 @@ def schain_config(): @schains_bp.route(get_api_url(BLUEPRINT_NAME, 'list'), methods=['GET']) @g_skale def schains_list(): - logger.debug(request) logger.debug(request) node_id = g.config.id if node_id is None: