From ccb850eec2a57392a63d78bee68266f71bde374c Mon Sep 17 00:00:00 2001 From: badrogger Date: Fri, 4 Oct 2024 19:16:20 +0000 Subject: [PATCH 1/9] Check that is safe to update --- node_cli/cli/node.py | 5 +++-- node_cli/core/node.py | 15 ++++++++++++++- node_cli/utils/exit_codes.py | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/node_cli/cli/node.py b/node_cli/cli/node.py index 95437b6b..57d09a15 100644 --- a/node_cli/cli/node.py +++ b/node_cli/cli/node.py @@ -109,10 +109,11 @@ def init_node(env_file): expose_value=False, prompt='Are you sure you want to update SKALE node software?') @click.option('--pull-config', 'pull_config_for_schain', hidden=True, type=str) +@click.option('--unsafe', 'unsafe_ok', hidden=True, type=str) @click.argument('env_file') @streamed_cmd -def update_node(env_file, pull_config_for_schain): - update(env_file, pull_config_for_schain) +def update_node(env_file, pull_config_for_schain, unsafe_ok): + update(env_file, pull_config_for_schain, unsafe_ok) @node.command('signature', help='Get node signature for given validator id') diff --git a/node_cli/core/node.py b/node_cli/core/node.py index 9d5c83f7..b3ff8245 100644 --- a/node_cli/core/node.py +++ b/node_cli/core/node.py @@ -92,6 +92,15 @@ class NodeStatuses(Enum): NOT_CREATED = 5 +def is_update_safe() -> bool: + response = get_request(BLUEPRINT_NAME, 'update-safe') + payload = response['payload'] + safe = payload['update_safe'] + if not safe: + logger.info('Locked schains: %s', payload['locked_schains']) + return safe + + @check_inited @check_user def register_node(name, p2p_ip, @@ -259,7 +268,11 @@ def get_node_env( @check_inited @check_user -def update(env_filepath, pull_config_for_schain): +def update(env_filepath, pull_config_for_schain, unsafe_ok: bool = False): + if not unsafe_ok and not is_update_safe(): + error_msg = 'Cannot update safetly' + error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) + logger.info('Node update started') configure_firewall_rules() env = get_node_env( diff --git a/node_cli/utils/exit_codes.py b/node_cli/utils/exit_codes.py index 1173aad0..85656fb1 100644 --- a/node_cli/utils/exit_codes.py +++ b/node_cli/utils/exit_codes.py @@ -30,3 +30,4 @@ class CLIExitCodes(IntEnum): REVERT_ERROR = 6 BAD_USER_ERROR = 7 NODE_STATE_ERROR = 8 + UNSAFE_UPDATE = 9 From 3a0c3ff99bc9a926f0772273239bc727724d189c Mon Sep 17 00:00:00 2001 From: badrogger Date: Mon, 7 Oct 2024 17:43:49 +0000 Subject: [PATCH 2/9] Fix safe update check. Add tests --- node_cli/cli/node.py | 19 ++++++-- node_cli/cli/sync_node.py | 9 +++- node_cli/configs/routes.py | 30 +++++++++--- node_cli/core/node.py | 19 +++++--- node_cli/utils/helper.py | 3 +- tests/core/core_node_test.py | 91 ++++++++++++++++++------------------ 6 files changed, 108 insertions(+), 63 deletions(-) diff --git a/node_cli/cli/node.py b/node_cli/cli/node.py index 57d09a15..3c907f42 100644 --- a/node_cli/cli/node.py +++ b/node_cli/cli/node.py @@ -109,7 +109,13 @@ def init_node(env_file): expose_value=False, prompt='Are you sure you want to update SKALE node software?') @click.option('--pull-config', 'pull_config_for_schain', hidden=True, type=str) -@click.option('--unsafe', 'unsafe_ok', hidden=True, type=str) +@click.option( + '--unsafe', + 'unsafe_ok', + help='Allow unsafe turn-off', + hidden=True, + is_flag=True +) @click.argument('env_file') @streamed_cmd def update_node(env_file, pull_config_for_schain, unsafe_ok): @@ -174,9 +180,16 @@ def remove_node_from_maintenance(): @click.option('--yes', is_flag=True, callback=abort_if_false, expose_value=False, prompt='Are you sure you want to turn off the node?') +@click.option( + '--unsafe', + 'unsafe_ok', + help='Allow unsafe turn-off', + hidden=True, + is_flag=True +) @streamed_cmd -def _turn_off(maintenance_on): - turn_off(maintenance_on) +def _turn_off(maintenance_on, unsafe_ok): + turn_off(maintenance_on, unsafe_ok) @node.command('turn-on', help='Turn on the node') diff --git a/node_cli/cli/sync_node.py b/node_cli/cli/sync_node.py index bca887b6..5a541e3f 100644 --- a/node_cli/cli/sync_node.py +++ b/node_cli/cli/sync_node.py @@ -74,7 +74,14 @@ def _init_sync(env_file, archive, catchup, historic_state): @click.option('--yes', is_flag=True, callback=abort_if_false, expose_value=False, prompt='Are you sure you want to update SKALE node software?') +@click.option( + '--unsafe', + 'unsafe_ok', + help='Allow unsafe turn-off', + hidden=True, + is_flag=True +) @click.argument('env_file') @streamed_cmd -def _update_sync(env_file): +def _update_sync(env_file, unsafe_ok): update_sync(env_file) diff --git a/node_cli/configs/routes.py b/node_cli/configs/routes.py index 87fac8f5..285e56bd 100644 --- a/node_cli/configs/routes.py +++ b/node_cli/configs/routes.py @@ -25,12 +25,22 @@ ROUTES = { 'v1': { - 'node': ['info', 'register', 'maintenance-on', 'maintenance-off', 'signature', - 'send-tg-notification', 'exit/start', 'exit/status', 'set-domain-name'], + 'node': [ + 'info', + 'register', + 'maintenance-on', + 'maintenance-off', + 'signature', + 'send-tg-notification', + 'exit/start', + 'exit/status', + 'set-domain-name', + 'update-safe', + ], 'health': ['containers', 'schains', 'sgx'], 'schains': ['config', 'list', 'dkg-statuses', 'firewall-rules', 'repair', 'get'], 'ssl': ['status', 'upload'], - 'wallet': ['info', 'send-eth'] + 'wallet': ['info', 'send-eth'], } } @@ -40,8 +50,11 @@ class RouteNotFoundException(Exception): def route_exists(blueprint, method, api_version): - return ROUTES.get(api_version) and ROUTES[api_version].get(blueprint) and \ - method in ROUTES[api_version][blueprint] + return ( + ROUTES.get(api_version) + and ROUTES[api_version].get(blueprint) + and method in ROUTES[api_version][blueprint] + ) def get_route(blueprint, method, api_version=CURRENT_API_VERSION, check=True): @@ -53,5 +66,8 @@ def get_route(blueprint, method, api_version=CURRENT_API_VERSION, check=True): def get_all_available_routes(api_version=CURRENT_API_VERSION): routes = ROUTES[api_version] - return [get_route(blueprint, method, api_version) for blueprint in routes - for method in routes[blueprint]] + return [ + get_route(blueprint, method, api_version) + for blueprint in routes + for method in routes[blueprint] + ] diff --git a/node_cli/core/node.py b/node_cli/core/node.py index b3ff8245..d47cb0b5 100644 --- a/node_cli/core/node.py +++ b/node_cli/core/node.py @@ -93,11 +93,12 @@ class NodeStatuses(Enum): def is_update_safe() -> bool: - response = get_request(BLUEPRINT_NAME, 'update-safe') - payload = response['payload'] + status, payload = get_request(BLUEPRINT_NAME, 'update-safe') + if status == 'error': + return False safe = payload['update_safe'] if not safe: - logger.info('Locked schains: %s', payload['locked_schains']) + logger.info('Locked schains: %s', payload['unsafe_chains']) return safe @@ -215,7 +216,10 @@ def init_sync( @check_inited @check_user -def update_sync(env_filepath): +def update_sync(env_filepath: str, unsafe_ok: bool = False) -> None: + if not unsafe_ok and not is_update_safe(): + error_msg = 'Cannot update safetly' + error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) logger.info('Node update started') configure_firewall_rules() env = get_node_env(env_filepath, sync_node=True) @@ -268,7 +272,7 @@ def get_node_env( @check_inited @check_user -def update(env_filepath, pull_config_for_schain, unsafe_ok: bool = False): +def update(env_filepath: str, pull_config_for_schain: str, unsafe_ok: bool = False) -> None: if not unsafe_ok and not is_update_safe(): error_msg = 'Cannot update safetly' error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) @@ -401,7 +405,10 @@ def set_maintenance_mode_off(): @check_inited @check_user -def turn_off(maintenance_on): +def turn_off(maintenance_on: bool = False, unsafe_ok: bool = False) -> None: + if not unsafe_ok and not is_update_safe(): + error_msg = 'Cannot turn off safetly' + error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) if maintenance_on: set_maintenance_mode_on() turn_off_op() diff --git a/node_cli/utils/helper.py b/node_cli/utils/helper.py index cba65ddd..71e559cf 100644 --- a/node_cli/utils/helper.py +++ b/node_cli/utils/helper.py @@ -24,6 +24,7 @@ import sys import uuid from urllib.parse import urlparse +from typing import Optional import yaml import shutil @@ -230,7 +231,7 @@ def post_request(blueprint, method, json=None, files=None): return status, payload -def get_request(blueprint, method, params=None): +def get_request(blueprint: str, method: str, params: Optional[dict] = None) -> tuple[str, str]: route = get_route(blueprint, method) url = construct_url(route) try: diff --git a/tests/core/core_node_test.py b/tests/core/core_node_test.py index 2ee12036..329e2669 100644 --- a/tests/core/core_node_test.py +++ b/tests/core/core_node_test.py @@ -12,12 +12,9 @@ from node_cli.configs import NODE_DATA_PATH from node_cli.configs.resource_allocation import RESOURCE_ALLOCATION_FILEPATH from node_cli.core.node import BASE_CONTAINERS_AMOUNT, is_base_containers_alive -from node_cli.core.node import init, pack_dir, update +from node_cli.core.node import init, pack_dir, update, is_update_safe -from tests.helper import ( - response_mock, - subprocess_run_mock -) +from tests.helper import response_mock, subprocess_run_mock from tests.resources_test import BIG_DISK_SIZE dclient = docker.from_env() @@ -30,8 +27,7 @@ @pytest.fixture def skale_base_containers(): containers = [ - dclient.containers.run(ALPINE_IMAGE_NAME, detach=True, - name=f'skale_test{i}', command=CMD) + dclient.containers.run(ALPINE_IMAGE_NAME, detach=True, name=f'skale_test{i}', command=CMD) for i in range(BASE_CONTAINERS_AMOUNT) ] yield containers @@ -42,8 +38,7 @@ def skale_base_containers(): @pytest.fixture def skale_base_containers_without_one(): containers = [ - dclient.containers.run(ALPINE_IMAGE_NAME, detach=True, - name=f'skale_test{i}', command=CMD) + dclient.containers.run(ALPINE_IMAGE_NAME, detach=True, name=f'skale_test{i}', command=CMD) for i in range(BASE_CONTAINERS_AMOUNT - 1) ] yield containers @@ -54,8 +49,7 @@ def skale_base_containers_without_one(): @pytest.fixture def skale_base_containers_exited(): containers = [ - dclient.containers.run(HELLO_WORLD_IMAGE_NAME, detach=True, - name=f'skale_test{i}') + dclient.containers.run(HELLO_WORLD_IMAGE_NAME, detach=True, name=f'skale_test{i}') for i in range(BASE_CONTAINERS_AMOUNT) ] time.sleep(10) @@ -92,18 +86,14 @@ def test_pack_dir(tmp_dir): print(tar.getnames()) assert Path(a_data).relative_to(tmp_dir).as_posix() in tar.getnames() assert Path(b_data).relative_to(tmp_dir).as_posix() in tar.getnames() - assert Path(trash_data).relative_to(tmp_dir).as_posix() in \ - tar.getnames() + assert Path(trash_data).relative_to(tmp_dir).as_posix() in tar.getnames() - cleaned_archive_path = os.path.abspath( - os.path.join(tmp_dir, 'cleaned-archive.tar.gz') - ) + cleaned_archive_path = os.path.abspath(os.path.join(tmp_dir, 'cleaned-archive.tar.gz')) pack_dir(backup_dir, cleaned_archive_path, exclude=(trash_dir,)) with tarfile.open(cleaned_archive_path) as tar: assert Path(a_data).relative_to(tmp_dir).as_posix() in tar.getnames() assert Path(b_data).relative_to(tmp_dir).as_posix() in tar.getnames() - assert Path(trash_data).relative_to(tmp_dir).as_posix() not in \ - tar.getnames() + assert Path(trash_data).relative_to(tmp_dir).as_posix() not in tar.getnames() # Not absolute or unrelated path in exclude raises ValueError with pytest.raises(ValueError): @@ -116,9 +106,7 @@ def test_is_base_containers_alive(skale_base_containers): assert is_base_containers_alive() -def test_is_base_containers_alive_one_failed( - skale_base_containers_without_one -): +def test_is_base_containers_alive_one_failed(skale_base_containers_without_one): assert not is_base_containers_alive() @@ -153,17 +141,15 @@ def test_init_node(no_resource_file): # todo: write new init node test resp_mock = response_mock(requests.codes.created) assert not os.path.isfile(RESOURCE_ALLOCATION_FILEPATH) env_filepath = './tests/test-env' - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.resources.get_disk_size', - return_value=BIG_DISK_SIZE), \ - mock.patch('node_cli.core.host.prepare_host'), \ - mock.patch('node_cli.core.host.init_data_dir'), \ - mock.patch('node_cli.core.node.configure_firewall_rules'), \ - mock.patch('node_cli.core.node.init_op'), \ - mock.patch('node_cli.core.node.is_base_containers_alive', - return_value=True), \ - mock.patch('node_cli.utils.helper.post_request', - resp_mock): + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE + ), mock.patch('node_cli.core.host.prepare_host'), mock.patch( + 'node_cli.core.host.init_data_dir' + ), mock.patch('node_cli.core.node.configure_firewall_rules'), mock.patch( + 'node_cli.core.node.init_op' + ), mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), mock.patch( + 'node_cli.utils.helper.post_request', resp_mock + ): init(env_filepath) assert os.path.isfile(RESOURCE_ALLOCATION_FILEPATH) @@ -172,17 +158,32 @@ def test_update_node(mocked_g_config, resource_file): env_filepath = './tests/test-env' resp_mock = response_mock(requests.codes.created) os.makedirs(NODE_DATA_PATH, exist_ok=True) - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.node.update_op'), \ - mock.patch('node_cli.core.node.get_flask_secret_key'), \ - mock.patch('node_cli.core.node.save_env_params'), \ - mock.patch('node_cli.core.node.configure_firewall_rules'), \ - mock.patch('node_cli.core.host.prepare_host'), \ - mock.patch('node_cli.core.node.is_base_containers_alive', - return_value=True), \ - mock.patch('node_cli.utils.helper.post_request', - resp_mock), \ - mock.patch('node_cli.core.resources.get_disk_size', - return_value=BIG_DISK_SIZE), \ - mock.patch('node_cli.core.host.init_data_dir'): + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.node.update_op' + ), mock.patch('node_cli.core.node.get_flask_secret_key'), mock.patch( + 'node_cli.core.node.save_env_params' + ), mock.patch('node_cli.core.node.configure_firewall_rules'), mock.patch( + 'node_cli.core.host.prepare_host' + ), mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), mock.patch( + 'node_cli.utils.helper.post_request', resp_mock + ), mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), mock.patch( + 'node_cli.core.host.init_data_dir' + ): update(env_filepath, pull_config_for_schain=None) + + +def test_is_update_safe(): + assert not is_update_safe() + safe_response = response_mock( + requests.codes.ok, + {'status': 'ok', 'payload': {'update_safe': True, 'unsafe_chains': []}}, + ) + with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_response): + assert is_update_safe() + + unsafe_response = response_mock( + requests.codes.ok, + {'status': 'ok', 'payload': {'update_safe': False, 'unsafe_chains': ['test_chain']}}, + ) + with mock.patch('node_cli.utils.helper.requests.get', return_value=unsafe_response): + assert not is_update_safe() From fb32776f6ea23358de02495cfc7c2712de22e9a9 Mon Sep 17 00:00:00 2001 From: badrogger Date: Tue, 8 Oct 2024 14:35:29 +0000 Subject: [PATCH 3/9] Fix tests --- tests/cli/node_test.py | 20 ++++-- tests/cli/sync_node_test.py | 120 +++++++++++++++++++---------------- tests/core/core_node_test.py | 21 +++--- tests/helper.py | 15 +++++ tests/routes_test.py | 1 + 5 files changed, 105 insertions(+), 72 deletions(-) diff --git a/tests/cli/node_test.py b/tests/cli/node_test.py index 319137ed..ec09d768 100644 --- a/tests/cli/node_test.py +++ b/tests/cli/node_test.py @@ -38,12 +38,14 @@ _turn_on, _set_domain_name ) +from node_cli.utils.exit_codes import CLIExitCodes from node_cli.utils.helper import init_default_logger from tests.helper import ( response_mock, run_command, run_command_mock, + safe_update_api_response, subprocess_run_mock ) from tests.resources_test import BIG_DISK_SIZE @@ -123,7 +125,6 @@ def test_register_with_no_alloc(mocked_g_config): register_node, ['--name', 'test-node', '-d', 'skale.test'], input='0.0.0.0\n') assert result.exit_code == 8 - print(repr(result.output)) assert result.output == f'Enter node public IP: 0.0.0.0\nCommand failed with following errors:\n--------------------------------------------------\nNode hasn\'t been inited before.\nYou should run < skale node init >\n--------------------------------------------------\nYou can find more info in {G_CONF_HOME}.skale/.skale-cli-log/debug-node-cli.log\n' # noqa @@ -375,7 +376,18 @@ def test_turn_off_maintenance_on(mocked_g_config): ) with mock.patch('subprocess.run', new=subprocess_run_mock), \ mock.patch('node_cli.core.node.turn_off_op'), \ - mock.patch('node_cli.core.node.is_node_inited', return_value=True): + mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): + with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_update_api_response()): + result = run_command_mock( + 'node_cli.utils.helper.requests.post', + resp_mock, + _turn_off, + [ + '--maintenance-on', + '--yes' + ]) + assert result.output == 'Setting maintenance mode on...\nNode is successfully set in maintenance mode\n' # noqa + assert result.exit_code == 0 result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, @@ -384,8 +396,8 @@ def test_turn_off_maintenance_on(mocked_g_config): '--maintenance-on', '--yes' ]) - assert result.exit_code == 0 - assert result.output == 'Setting maintenance mode on...\nNode is successfully set in maintenance mode\n' # noqa + assert 'Cannot turn off safetly' in result.output + assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE def test_turn_on_maintenance_off(mocked_g_config): diff --git a/tests/cli/sync_node_test.py b/tests/cli/sync_node_test.py index 3966d3c8..36c7dd75 100644 --- a/tests/cli/sync_node_test.py +++ b/tests/cli/sync_node_test.py @@ -23,13 +23,12 @@ import logging from node_cli.configs import SKALE_DIR, NODE_DATA_PATH +from node_cli.core.node_options import NodeOptions from node_cli.cli.sync_node import _init_sync, _update_sync +from node_cli.utils.exit_codes import CLIExitCodes from node_cli.utils.helper import init_default_logger -from node_cli.core.node_options import NodeOptions -from tests.helper import ( - run_command, subprocess_run_mock -) +from tests.helper import run_command, safe_update_api_response, subprocess_run_mock from tests.resources_test import BIG_DISK_SIZE logger = logging.getLogger(__name__) @@ -38,43 +37,41 @@ def test_init_sync(mocked_g_config): pathlib.Path(SKALE_DIR).mkdir(parents=True, exist_ok=True) - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.node.init_sync_op'), \ - mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), \ - mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), \ - mock.patch('node_cli.core.node.configure_firewall_rules'), \ - mock.patch('node_cli.utils.decorators.is_node_inited', return_value=False): - result = run_command( - _init_sync, - ['./tests/test-env'] - ) + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.node.init_sync_op' + ), mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), mock.patch( + 'node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE + ), mock.patch('node_cli.core.node.configure_firewall_rules'), mock.patch( + 'node_cli.utils.decorators.is_node_inited', return_value=False + ): + result = run_command(_init_sync, ['./tests/test-env']) assert result.exit_code == 0 def test_init_sync_archive_catchup(mocked_g_config, clean_node_options): pathlib.Path(NODE_DATA_PATH).mkdir(parents=True, exist_ok=True) -# with mock.patch('subprocess.run', new=subprocess_run_mock), \ - with mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), \ - mock.patch('node_cli.operations.base.cleanup_volume_artifacts'), \ - mock.patch('node_cli.operations.base.download_skale_node'), \ - mock.patch('node_cli.operations.base.sync_skale_node'), \ - mock.patch('node_cli.operations.base.configure_docker'), \ - mock.patch('node_cli.operations.base.prepare_host'), \ - mock.patch('node_cli.operations.base.ensure_filestorage_mapping'), \ - mock.patch('node_cli.operations.base.link_env_file'), \ - mock.patch('node_cli.operations.base.download_contracts'), \ - mock.patch('node_cli.operations.base.generate_nginx_config'), \ - mock.patch('node_cli.operations.base.prepare_block_device'), \ - mock.patch('node_cli.operations.base.update_meta'), \ - mock.patch('node_cli.operations.base.update_resource_allocation'), \ - mock.patch('node_cli.operations.base.update_images'), \ - mock.patch('node_cli.operations.base.compose_up'), \ - mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), \ - mock.patch('node_cli.core.node.configure_firewall_rules'), \ - mock.patch('node_cli.utils.decorators.is_node_inited', return_value=False): + # with mock.patch('subprocess.run', new=subprocess_run_mock), \ + with mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), mock.patch( + 'node_cli.operations.base.cleanup_volume_artifacts' + ), mock.patch('node_cli.operations.base.download_skale_node'), mock.patch( + 'node_cli.operations.base.sync_skale_node' + ), mock.patch('node_cli.operations.base.configure_docker'), mock.patch( + 'node_cli.operations.base.prepare_host' + ), mock.patch('node_cli.operations.base.ensure_filestorage_mapping'), mock.patch( + 'node_cli.operations.base.link_env_file' + ), mock.patch('node_cli.operations.base.download_contracts'), mock.patch( + 'node_cli.operations.base.generate_nginx_config' + ), mock.patch('node_cli.operations.base.prepare_block_device'), mock.patch( + 'node_cli.operations.base.update_meta' + ), mock.patch('node_cli.operations.base.update_resource_allocation'), mock.patch( + 'node_cli.operations.base.update_images' + ), mock.patch('node_cli.operations.base.compose_up'), mock.patch( + 'node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE + ), mock.patch('node_cli.core.node.configure_firewall_rules'), mock.patch( + 'node_cli.utils.decorators.is_node_inited', return_value=False + ): result = run_command( - _init_sync, - ['./tests/test-env', '--archive', '--catchup', '--historic-state'] + _init_sync, ['./tests/test-env', '--archive', '--catchup', '--historic-state'] ) node_options = NodeOptions() @@ -87,30 +84,41 @@ def test_init_sync_archive_catchup(mocked_g_config, clean_node_options): def test_init_sync_historic_state_fail(mocked_g_config, clean_node_options): pathlib.Path(SKALE_DIR).mkdir(parents=True, exist_ok=True) - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.node.init_sync_op'), \ - mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), \ - mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), \ - mock.patch('node_cli.core.node.configure_firewall_rules'), \ - mock.patch('node_cli.utils.decorators.is_node_inited', return_value=False): - result = run_command( - _init_sync, - ['./tests/test-env', '--historic-state'] - ) + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.node.init_sync_op' + ), mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), mock.patch( + 'node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE + ), mock.patch('node_cli.core.node.configure_firewall_rules'), mock.patch( + 'node_cli.utils.decorators.is_node_inited', return_value=False + ): + result = run_command(_init_sync, ['./tests/test-env', '--historic-state']) assert result.exit_code == 1 assert '--historic-state can be used only' in result.output def test_update_sync(mocked_g_config): pathlib.Path(SKALE_DIR).mkdir(parents=True, exist_ok=True) - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.node.update_sync_op'), \ - mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), \ - mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), \ - mock.patch('node_cli.core.node.configure_firewall_rules'), \ - mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): - result = run_command( - _update_sync, - ['./tests/test-env', '--yes'] - ) - assert result.exit_code == 0 + + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.node.update_sync_op' + ), mock.patch('node_cli.core.node.is_base_containers_alive', return_value=True), mock.patch( + 'node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE + ), mock.patch('node_cli.core.node.configure_firewall_rules'), mock.patch( + 'node_cli.utils.decorators.is_node_inited', return_value=True + ): + result = run_command(_update_sync, ['./tests/test-env', '--yes']) + assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE + assert 'Cannot update safetly' in result.output + + with mock.patch( + 'node_cli.utils.helper.requests.get', return_value=safe_update_api_response() + ): + result = run_command(_update_sync, ['./tests/test-env', '--yes']) + assert result.exit_code == 0 + + with mock.patch( + 'node_cli.utils.helper.requests.get', return_value=safe_update_api_response(False) + ): + result = run_command(_update_sync, ['./tests/test-env', '--yes']) + assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE + assert 'Cannot update safetly' in result.output diff --git a/tests/core/core_node_test.py b/tests/core/core_node_test.py index 329e2669..7f699645 100644 --- a/tests/core/core_node_test.py +++ b/tests/core/core_node_test.py @@ -13,8 +13,9 @@ from node_cli.configs.resource_allocation import RESOURCE_ALLOCATION_FILEPATH from node_cli.core.node import BASE_CONTAINERS_AMOUNT, is_base_containers_alive from node_cli.core.node import init, pack_dir, update, is_update_safe +from node_cli.utils.exit_codes import CLIExitCodes -from tests.helper import response_mock, subprocess_run_mock +from tests.helper import response_mock, safe_update_api_response, subprocess_run_mock from tests.resources_test import BIG_DISK_SIZE dclient = docker.from_env() @@ -169,21 +170,17 @@ def test_update_node(mocked_g_config, resource_file): ), mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), mock.patch( 'node_cli.core.host.init_data_dir' ): - update(env_filepath, pull_config_for_schain=None) + with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_update_api_response()): + result = update(env_filepath, pull_config_for_schain=None) + assert result is None def test_is_update_safe(): assert not is_update_safe() - safe_response = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': {'update_safe': True, 'unsafe_chains': []}}, - ) - with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_response): + with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_update_api_response()): assert is_update_safe() - unsafe_response = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': {'update_safe': False, 'unsafe_chains': ['test_chain']}}, - ) - with mock.patch('node_cli.utils.helper.requests.get', return_value=unsafe_response): + with mock.patch( + 'node_cli.utils.helper.requests.get', return_value=safe_update_api_response(safe=False) + ): assert not is_update_safe() diff --git a/tests/helper.py b/tests/helper.py index 832ac577..c753e176 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -20,6 +20,8 @@ import mock import os + +import requests from click.testing import CliRunner from mock import Mock, MagicMock @@ -84,3 +86,16 @@ def subprocess_run_mock(*args, returncode=0, **kwargs): result.stdout = MagicMock() result.stderr = MagicMock() return result + + +def safe_update_api_response(safe: bool = True) -> dict: + if safe: + return response_mock( + requests.codes.ok, + {'status': 'ok', 'payload': {'update_safe': True, 'unsafe_chains': []}}, + ) + else: + return response_mock( + requests.codes.ok, + {'status': 'ok', 'payload': {'update_safe': False, 'unsafe_chains': ['test_chain']}}, + ) diff --git a/tests/routes_test.py b/tests/routes_test.py index 3cd2416e..9c00b8f1 100644 --- a/tests/routes_test.py +++ b/tests/routes_test.py @@ -13,6 +13,7 @@ '/api/v1/node/exit/start', '/api/v1/node/exit/status', '/api/v1/node/set-domain-name', + '/api/v1/node/update-safe', '/api/v1/health/containers', '/api/v1/health/schains', From bc3f44680f56171a61ef7ad2b4e52af2e2fdc1ca Mon Sep 17 00:00:00 2001 From: badrogger Date: Tue, 8 Oct 2024 15:04:15 +0000 Subject: [PATCH 4/9] Fix linter --- tests/cli/node_test.py | 351 ++++++++++++++++++----------------- tests/core/core_node_test.py | 3 +- 2 files changed, 177 insertions(+), 177 deletions(-) diff --git a/tests/cli/node_test.py b/tests/cli/node_test.py index ec09d768..d1bc5f34 100644 --- a/tests/cli/node_test.py +++ b/tests/cli/node_test.py @@ -36,7 +36,7 @@ version, _turn_off, _turn_on, - _set_domain_name + _set_domain_name, ) from node_cli.utils.exit_codes import CLIExitCodes from node_cli.utils.helper import init_default_logger @@ -46,7 +46,7 @@ run_command, run_command_mock, safe_update_api_response, - subprocess_run_mock + subprocess_run_mock, ) from tests.resources_test import BIG_DISK_SIZE @@ -55,18 +55,19 @@ def test_register_node(resource_alloc, mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) with mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, register_node, - ['--name', 'test-node', '--ip', '0.0.0.0', '--port', '8080', '-d', 'skale.test']) + ['--name', 'test-node', '--ip', '0.0.0.0', '--port', '8080', '-d', 'skale.test'], + ) assert result.exit_code == 0 - assert result.output == 'Node registered in SKALE manager.\nFor more info run < skale node info >\n' # noqa + assert ( + result.output + == 'Node registered in SKALE manager.\nFor more info run < skale node info >\n' + ) # noqa def test_register_node_with_error(resource_alloc, mocked_g_config): @@ -79,98 +80,99 @@ def test_register_node_with_error(resource_alloc, mocked_g_config): 'node_cli.utils.helper.requests.post', resp_mock, register_node, - ['--name', 'test-node2', '--ip', '0.0.0.0', '--port', '80', '-d', 'skale.test']) + ['--name', 'test-node2', '--ip', '0.0.0.0', '--port', '80', '-d', 'skale.test'], + ) assert result.exit_code == 3 - assert result.output == f'Command failed with following errors:\n--------------------------------------------------\nStrange error\n--------------------------------------------------\nYou can find more info in {G_CONF_HOME}.skale/.skale-cli-log/debug-node-cli.log\n' # noqa + assert ( + result.output == f'Command failed with following errors:\n--------------------------------------------------\nStrange error\n--------------------------------------------------\nYou can find more info in {G_CONF_HOME}.skale/.skale-cli-log/debug-node-cli.log\n') # noqa def test_register_node_with_prompted_ip(resource_alloc, mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) with mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, register_node, - ['--name', 'test-node', '--port', '8080', '-d', 'skale.test'], input='0.0.0.0\n') + ['--name', 'test-node', '--port', '8080', '-d', 'skale.test'], + input='0.0.0.0\n', + ) assert result.exit_code == 0 assert result.output == 'Enter node public IP: 0.0.0.0\nNode registered in SKALE manager.\nFor more info run < skale node info >\n' # noqa def test_register_node_with_default_port(resource_alloc, mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) with mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, register_node, - ['--name', 'test-node', '-d', 'skale.test'], input='0.0.0.0\n') + ['--name', 'test-node', '-d', 'skale.test'], + input='0.0.0.0\n', + ) assert result.exit_code == 0 assert result.output == 'Enter node public IP: 0.0.0.0\nNode registered in SKALE manager.\nFor more info run < skale node info >\n' # noqa def test_register_with_no_alloc(mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, register_node, - ['--name', 'test-node', '-d', 'skale.test'], input='0.0.0.0\n') + ['--name', 'test-node', '-d', 'skale.test'], + input='0.0.0.0\n', + ) assert result.exit_code == 8 - assert result.output == f'Enter node public IP: 0.0.0.0\nCommand failed with following errors:\n--------------------------------------------------\nNode hasn\'t been inited before.\nYou should run < skale node init >\n--------------------------------------------------\nYou can find more info in {G_CONF_HOME}.skale/.skale-cli-log/debug-node-cli.log\n' # noqa + assert result.output == f"Enter node public IP: 0.0.0.0\nCommand failed with following errors:\n--------------------------------------------------\nNode hasn't been inited before.\nYou should run < skale node init >\n--------------------------------------------------\nYou can find more info in {G_CONF_HOME}.skale/.skale-cli-log/debug-node-cli.log\n" # noqa def test_node_info_node_info(): payload = { 'node_info': { - 'name': 'test', 'ip': '0.0.0.0', + 'name': 'test', + 'ip': '0.0.0.0', 'publicIP': '1.1.1.1', 'port': 10001, 'publicKey': '0x7', 'start_date': 1570114466, 'leaving_date': 0, - 'last_reward_date': 1570628924, 'second_address': 0, - 'status': 0, 'id': 32, 'owner': '0x23', - 'domain_name': 'skale.test' + 'last_reward_date': 1570628924, + 'second_address': 0, + 'status': 0, + 'id': 32, + 'owner': '0x23', + 'domain_name': 'skale.test', } } - resp_mock = response_mock( - requests.codes.ok, - json_data={'payload': payload, 'status': 'ok'} - ) + resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Active\n--------------------------------------------------\n' # noqa + assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Active\n--------------------------------------------------\n') # noqa def test_node_info_node_info_not_created(): payload = { 'node_info': { - 'name': 'test', 'ip': '0.0.0.0', + 'name': 'test', + 'ip': '0.0.0.0', 'publicIP': '1.1.1.1', 'port': 10001, 'publicKey': '0x7', 'start_date': 1570114466, 'leaving_date': 0, - 'last_reward_date': 1570628924, 'second_address': 0, - 'status': 5, 'id': 32, 'owner': '0x23', - 'domain_name': 'skale.test' + 'last_reward_date': 1570628924, + 'second_address': 0, + 'status': 5, + 'id': 32, + 'owner': '0x23', + 'domain_name': 'skale.test', } } - resp_mock = response_mock( - requests.codes.ok, - json_data={'payload': payload, 'status': 'ok'} - ) + resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 assert result.output == 'This SKALE node is not registered on SKALE Manager yet\n' @@ -179,118 +181,127 @@ def test_node_info_node_info_not_created(): def test_node_info_node_info_frozen(): payload = { 'node_info': { - 'name': 'test', 'ip': '0.0.0.0', + 'name': 'test', + 'ip': '0.0.0.0', 'publicIP': '1.1.1.1', 'port': 10001, 'publicKey': '0x7', 'start_date': 1570114466, 'leaving_date': 0, - 'last_reward_date': 1570628924, 'second_address': 0, - 'status': 2, 'id': 32, 'owner': '0x23', - 'domain_name': 'skale.test' + 'last_reward_date': 1570628924, + 'second_address': 0, + 'status': 2, + 'id': 32, + 'owner': '0x23', + 'domain_name': 'skale.test', } } - resp_mock = response_mock( - requests.codes.ok, - json_data={'payload': payload, 'status': 'ok'} - ) + resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Frozen\n--------------------------------------------------\n' # noqa + assert ( + result.output + == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Frozen\n--------------------------------------------------\n' + ) # noqa def test_node_info_node_info_left(): payload = { 'node_info': { - 'name': 'test', 'ip': '0.0.0.0', + 'name': 'test', + 'ip': '0.0.0.0', 'publicIP': '1.1.1.1', 'port': 10001, 'publicKey': '0x7', 'start_date': 1570114466, 'leaving_date': 0, - 'last_reward_date': 1570628924, 'second_address': 0, - 'status': 4, 'id': 32, 'owner': '0x23', - 'domain_name': 'skale.test' + 'last_reward_date': 1570628924, + 'second_address': 0, + 'status': 4, + 'id': 32, + 'owner': '0x23', + 'domain_name': 'skale.test', } } - resp_mock = response_mock( - requests.codes.ok, - json_data={'payload': payload, 'status': 'ok'} - ) + resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Left\n--------------------------------------------------\n' # noqa + assert ( + result.output + == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Left\n--------------------------------------------------\n' + ) # noqa def test_node_info_node_info_leaving(): payload = { 'node_info': { - 'name': 'test', 'ip': '0.0.0.0', + 'name': 'test', + 'ip': '0.0.0.0', 'publicIP': '1.1.1.1', 'port': 10001, 'publicKey': '0x7', 'start_date': 1570114466, 'leaving_date': 0, - 'last_reward_date': 1570628924, 'second_address': 0, - 'status': 1, 'id': 32, 'owner': '0x23', - 'domain_name': 'skale.test' + 'last_reward_date': 1570628924, + 'second_address': 0, + 'status': 1, + 'id': 32, + 'owner': '0x23', + 'domain_name': 'skale.test', } } - resp_mock = response_mock( - requests.codes.ok, - json_data={'payload': payload, 'status': 'ok'} - ) + resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Leaving\n--------------------------------------------------\n' # noqa + assert ( + result.output + == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Leaving\n--------------------------------------------------\n' + ) # noqa def test_node_info_node_info_in_maintenance(): payload = { 'node_info': { - 'name': 'test', 'ip': '0.0.0.0', + 'name': 'test', + 'ip': '0.0.0.0', 'publicIP': '1.1.1.1', 'port': 10001, 'publicKey': '0x7', 'start_date': 1570114466, 'leaving_date': 0, - 'last_reward_date': 1570628924, 'second_address': 0, - 'status': 3, 'id': 32, 'owner': '0x23', - 'domain_name': 'skale.test' + 'last_reward_date': 1570628924, + 'second_address': 0, + 'status': 3, + 'id': 32, + 'owner': '0x23', + 'domain_name': 'skale.test', } } - resp_mock = response_mock( - requests.codes.ok, - json_data={'payload': payload, 'status': 'ok'} - ) + resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: In Maintenance\n--------------------------------------------------\n' # noqa + assert ( + result.output + == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: In Maintenance\n--------------------------------------------------\n' + ) # noqa def test_node_signature(): signature_sample = '0x1231231231' - response_data = { - 'status': 'ok', - 'payload': {'signature': signature_sample} - } + response_data = {'status': 'ok', 'payload': {'signature': signature_sample}} resp_mock = response_mock(requests.codes.ok, json_data=response_data) - result = run_command_mock('node_cli.utils.helper.requests.get', - resp_mock, signature, ['1']) + result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, signature, ['1']) assert result.exit_code == 0 assert result.output == f'Signature: {signature_sample}\n' def test_backup(): pathlib.Path(SKALE_DIR).mkdir(parents=True, exist_ok=True) - result = run_command( - backup_node, - ['/tmp'] - ) + result = run_command(backup_node, ['/tmp']) assert result.exit_code == 0 print(result.output) assert 'Backup archive succesfully created ' in result.output @@ -298,21 +309,17 @@ def test_backup(): def test_restore(mocked_g_config): pathlib.Path(SKALE_DIR).mkdir(parents=True, exist_ok=True) - result = run_command( - backup_node, - ['/tmp'] + result = run_command(backup_node, ['/tmp']) + backup_path = result.output.replace('Backup archive successfully created: ', '').replace( + '\n', '' ) - backup_path = result.output.replace( - 'Backup archive successfully created: ', '').replace('\n', '') - - with patch('node_cli.core.node.restore_op', MagicMock()) as mock_restore_op, \ - patch('subprocess.run', new=subprocess_run_mock), \ - patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), \ - patch('node_cli.utils.decorators.is_node_inited', return_value=False): - result = run_command( - restore_node, - [backup_path, './tests/test-env'] - ) + + with patch('node_cli.core.node.restore_op', MagicMock()) as mock_restore_op, patch( + 'subprocess.run', new=subprocess_run_mock + ), patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), patch( + 'node_cli.utils.decorators.is_node_inited', return_value=False + ): + result = run_command(restore_node, [backup_path, './tests/test-env']) assert result.exit_code == 0 assert 'Node is restored from backup\n' in result.output # noqa @@ -321,21 +328,17 @@ def test_restore(mocked_g_config): def test_restore_no_snapshot(mocked_g_config): pathlib.Path(SKALE_DIR).mkdir(parents=True, exist_ok=True) - result = run_command( - backup_node, - ['/tmp'] + result = run_command(backup_node, ['/tmp']) + backup_path = result.output.replace('Backup archive successfully created: ', '').replace( + '\n', '' ) - backup_path = result.output.replace( - 'Backup archive successfully created: ', '').replace('\n', '') - - with patch('node_cli.core.node.restore_op', MagicMock()) as mock_restore_op, \ - patch('subprocess.run', new=subprocess_run_mock), \ - patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), \ - patch('node_cli.utils.decorators.is_node_inited', return_value=False): - result = run_command( - restore_node, - [backup_path, './tests/test-env', '--no-snapshot'] - ) + + with patch('node_cli.core.node.restore_op', MagicMock()) as mock_restore_op, patch( + 'subprocess.run', new=subprocess_run_mock + ), patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), patch( + 'node_cli.utils.decorators.is_node_inited', return_value=False + ): + result = run_command(restore_node, [backup_path, './tests/test-env', '--no-snapshot']) assert result.exit_code == 0 assert 'Node is restored from backup\n' in result.output # noqa @@ -343,109 +346,107 @@ def test_restore_no_snapshot(mocked_g_config): def test_maintenance_on(): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) result = run_command_mock( - 'node_cli.utils.helper.requests.post', - resp_mock, - set_node_in_maintenance, - ['--yes']) + 'node_cli.utils.helper.requests.post', resp_mock, set_node_in_maintenance, ['--yes'] + ) assert result.exit_code == 0 - assert result.output == 'Setting maintenance mode on...\nNode is successfully set in maintenance mode\n' # noqa + assert ( + result.output + == 'Setting maintenance mode on...\nNode is successfully set in maintenance mode\n' + ) # noqa def test_maintenance_off(mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) result = run_command_mock( - 'node_cli.utils.helper.requests.post', - resp_mock, - remove_node_from_maintenance) + 'node_cli.utils.helper.requests.post', resp_mock, remove_node_from_maintenance + ) assert result.exit_code == 0 - assert result.output == 'Setting maintenance mode off...\nNode is successfully removed from maintenance mode\n' # noqa + assert ( + result.output + == 'Setting maintenance mode off...\nNode is successfully removed from maintenance mode\n' + ) # noqa def test_turn_off_maintenance_on(mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.node.turn_off_op'), \ - mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): - with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_update_api_response()): + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.node.turn_off_op' + ), mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): + with mock.patch( + 'node_cli.utils.helper.requests.get', return_value=safe_update_api_response() + ): result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, _turn_off, - [ - '--maintenance-on', - '--yes' - ]) - assert result.output == 'Setting maintenance mode on...\nNode is successfully set in maintenance mode\n' # noqa + ['--maintenance-on', '--yes'], + ) + assert ( + result.output + == 'Setting maintenance mode on...\nNode is successfully set in maintenance mode\n' + ) # noqa assert result.exit_code == 0 result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, _turn_off, - [ - '--maintenance-on', - '--yes' - ]) + ['--maintenance-on', '--yes'], + ) assert 'Cannot turn off safetly' in result.output assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE def test_turn_on_maintenance_off(mocked_g_config): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) - with mock.patch('subprocess.run', new=subprocess_run_mock), \ - mock.patch('node_cli.core.node.get_flask_secret_key'), \ - mock.patch('node_cli.core.node.turn_on_op'), \ - mock.patch('node_cli.core.node.is_base_containers_alive'), \ - mock.patch('node_cli.core.node.is_node_inited', return_value=True): + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) + with mock.patch('subprocess.run', new=subprocess_run_mock), mock.patch( + 'node_cli.core.node.get_flask_secret_key' + ), mock.patch('node_cli.core.node.turn_on_op'), mock.patch( + 'node_cli.core.node.is_base_containers_alive' + ), mock.patch('node_cli.core.node.is_node_inited', return_value=True): result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, _turn_on, - [ - './tests/test-env', - '--maintenance-off', - '--sync-schains', - '--yes' - ]) + ['./tests/test-env', '--maintenance-off', '--sync-schains', '--yes'], + ) assert result.exit_code == 0 - assert result.output == 'Setting maintenance mode off...\nNode is successfully removed from maintenance mode\n' # noqa, tmp fix + assert ( + result.output + == 'Setting maintenance mode off...\nNode is successfully removed from maintenance mode\n' + ) # noqa, tmp fix def test_set_domain_name(): - resp_mock = response_mock( - requests.codes.ok, - {'status': 'ok', 'payload': None} - ) + resp_mock = response_mock(requests.codes.ok, {'status': 'ok', 'payload': None}) with mock.patch('node_cli.utils.decorators.is_node_inited', return_value=True): result = run_command_mock( 'node_cli.utils.helper.requests.post', resp_mock, - _set_domain_name, ['-d', 'skale.test', '--yes']) + _set_domain_name, + ['-d', 'skale.test', '--yes'], + ) assert result.exit_code == 0 - assert result.output == 'Setting new domain name: skale.test\nDomain name successfully changed\n' # noqa + assert ( + result.output == 'Setting new domain name: skale.test\nDomain name successfully changed\n' + ) # noqa def test_node_version(meta_file_v2): result = run_command(version) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nVersion: 0.1.1\nConfig Stream: develop\nLvmpy stream: 1.1.2\n--------------------------------------------------\n' # noqa + assert ( + result.output + == '--------------------------------------------------\nVersion: 0.1.1\nConfig Stream: develop\nLvmpy stream: 1.1.2\n--------------------------------------------------\n' + ) # noqa result = run_command(version, ['--json']) print(repr(result.output)) assert result.exit_code == 0 - assert result.output == "{'version': '0.1.1', 'config_stream': 'develop', 'docker_lvmpy_stream': '1.1.2'}\n" # noqa + assert ( + result.output + == "{'version': '0.1.1', 'config_stream': 'develop', 'docker_lvmpy_stream': '1.1.2'}\n" + ) # noqa diff --git a/tests/core/core_node_test.py b/tests/core/core_node_test.py index 7f699645..c3c3e11d 100644 --- a/tests/core/core_node_test.py +++ b/tests/core/core_node_test.py @@ -13,7 +13,6 @@ from node_cli.configs.resource_allocation import RESOURCE_ALLOCATION_FILEPATH from node_cli.core.node import BASE_CONTAINERS_AMOUNT, is_base_containers_alive from node_cli.core.node import init, pack_dir, update, is_update_safe -from node_cli.utils.exit_codes import CLIExitCodes from tests.helper import response_mock, safe_update_api_response, subprocess_run_mock from tests.resources_test import BIG_DISK_SIZE @@ -170,7 +169,7 @@ def test_update_node(mocked_g_config, resource_file): ), mock.patch('node_cli.core.resources.get_disk_size', return_value=BIG_DISK_SIZE), mock.patch( 'node_cli.core.host.init_data_dir' ): - with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_update_api_response()): + with mock.patch('node_cli.utils.helper.requests.get', return_value=safe_update_api_response()): # noqa result = update(env_filepath, pull_config_for_schain=None) assert result is None From 4bf7b944ea02e7c86b1e0ea855a6e566f9db6bef Mon Sep 17 00:00:00 2001 From: badrogger Date: Tue, 8 Oct 2024 15:17:56 +0000 Subject: [PATCH 5/9] Fix tests --- tests/cli/node_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cli/node_test.py b/tests/cli/node_test.py index d1bc5f34..10816b33 100644 --- a/tests/cli/node_test.py +++ b/tests/cli/node_test.py @@ -150,7 +150,7 @@ def test_node_info_node_info(): resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Active\n--------------------------------------------------\n') # noqa + assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Active\n--------------------------------------------------\n' # noqa def test_node_info_node_info_not_created(): From dd82abe9bcc475b39831655537c3c9df11fb38fa Mon Sep 17 00:00:00 2001 From: badrogger Date: Tue, 8 Oct 2024 15:32:58 +0000 Subject: [PATCH 6/9] Fix linter --- tests/cli/node_test.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/cli/node_test.py b/tests/cli/node_test.py index 10816b33..7e393b27 100644 --- a/tests/cli/node_test.py +++ b/tests/cli/node_test.py @@ -200,10 +200,7 @@ def test_node_info_node_info_frozen(): resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert ( - result.output - == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Frozen\n--------------------------------------------------\n' - ) # noqa + assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Frozen\n--------------------------------------------------\n' # noqa def test_node_info_node_info_left(): @@ -228,10 +225,7 @@ def test_node_info_node_info_left(): resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert ( - result.output - == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Left\n--------------------------------------------------\n' - ) # noqa + assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Left\n--------------------------------------------------\n' # noqa def test_node_info_node_info_leaving(): @@ -256,10 +250,7 @@ def test_node_info_node_info_leaving(): resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert ( - result.output - == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Leaving\n--------------------------------------------------\n' - ) # noqa + assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: Leaving\n--------------------------------------------------\n' # noqa def test_node_info_node_info_in_maintenance(): @@ -284,10 +275,7 @@ def test_node_info_node_info_in_maintenance(): resp_mock = response_mock(requests.codes.ok, json_data={'payload': payload, 'status': 'ok'}) result = run_command_mock('node_cli.utils.helper.requests.get', resp_mock, node_info) assert result.exit_code == 0 - assert ( - result.output - == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: In Maintenance\n--------------------------------------------------\n' - ) # noqa + assert result.output == '--------------------------------------------------\nNode info\nName: test\nID: 32\nIP: 0.0.0.0\nPublic IP: 1.1.1.1\nPort: 10001\nDomain name: skale.test\nStatus: In Maintenance\n--------------------------------------------------\n' # noqa def test_node_signature(): @@ -438,10 +426,7 @@ def test_set_domain_name(): def test_node_version(meta_file_v2): result = run_command(version) assert result.exit_code == 0 - assert ( - result.output - == '--------------------------------------------------\nVersion: 0.1.1\nConfig Stream: develop\nLvmpy stream: 1.1.2\n--------------------------------------------------\n' - ) # noqa + assert result.output == '--------------------------------------------------\nVersion: 0.1.1\nConfig Stream: develop\nLvmpy stream: 1.1.2\n--------------------------------------------------\n' # noqa result = run_command(version, ['--json']) print(repr(result.output)) From d2e33df1cb398299a4079e3f85bad0fb68084d78 Mon Sep 17 00:00:00 2001 From: badrogger Date: Wed, 9 Oct 2024 12:35:12 +0000 Subject: [PATCH 7/9] Fix typo --- node_cli/core/node.py | 6 +++--- tests/cli/node_test.py | 2 +- tests/cli/sync_node_test.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/node_cli/core/node.py b/node_cli/core/node.py index d47cb0b5..80fa3e3e 100644 --- a/node_cli/core/node.py +++ b/node_cli/core/node.py @@ -218,7 +218,7 @@ def init_sync( @check_user def update_sync(env_filepath: str, unsafe_ok: bool = False) -> None: if not unsafe_ok and not is_update_safe(): - error_msg = 'Cannot update safetly' + error_msg = 'Cannot update safely' error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) logger.info('Node update started') configure_firewall_rules() @@ -274,7 +274,7 @@ def get_node_env( @check_user def update(env_filepath: str, pull_config_for_schain: str, unsafe_ok: bool = False) -> None: if not unsafe_ok and not is_update_safe(): - error_msg = 'Cannot update safetly' + error_msg = 'Cannot update safety' error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) logger.info('Node update started') @@ -407,7 +407,7 @@ def set_maintenance_mode_off(): @check_user def turn_off(maintenance_on: bool = False, unsafe_ok: bool = False) -> None: if not unsafe_ok and not is_update_safe(): - error_msg = 'Cannot turn off safetly' + error_msg = 'Cannot turn off safely' error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) if maintenance_on: set_maintenance_mode_on() diff --git a/tests/cli/node_test.py b/tests/cli/node_test.py index 7e393b27..9c86057c 100644 --- a/tests/cli/node_test.py +++ b/tests/cli/node_test.py @@ -382,7 +382,7 @@ def test_turn_off_maintenance_on(mocked_g_config): _turn_off, ['--maintenance-on', '--yes'], ) - assert 'Cannot turn off safetly' in result.output + assert 'Cannot turn off safely' in result.output assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE diff --git a/tests/cli/sync_node_test.py b/tests/cli/sync_node_test.py index 36c7dd75..84b4a423 100644 --- a/tests/cli/sync_node_test.py +++ b/tests/cli/sync_node_test.py @@ -108,7 +108,7 @@ def test_update_sync(mocked_g_config): ): result = run_command(_update_sync, ['./tests/test-env', '--yes']) assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE - assert 'Cannot update safetly' in result.output + assert 'Cannot update safely' in result.output with mock.patch( 'node_cli.utils.helper.requests.get', return_value=safe_update_api_response() @@ -121,4 +121,4 @@ def test_update_sync(mocked_g_config): ): result = run_command(_update_sync, ['./tests/test-env', '--yes']) assert result.exit_code == CLIExitCodes.UNSAFE_UPDATE - assert 'Cannot update safetly' in result.output + assert 'Cannot update safely' in result.output From f3348a179b4921edca33220293dca4a39f33539c Mon Sep 17 00:00:00 2001 From: badrogger Date: Wed, 9 Oct 2024 12:41:23 +0000 Subject: [PATCH 8/9] Fix typo --- node_cli/core/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node_cli/core/node.py b/node_cli/core/node.py index 80fa3e3e..e8895d2a 100644 --- a/node_cli/core/node.py +++ b/node_cli/core/node.py @@ -274,7 +274,7 @@ def get_node_env( @check_user def update(env_filepath: str, pull_config_for_schain: str, unsafe_ok: bool = False) -> None: if not unsafe_ok and not is_update_safe(): - error_msg = 'Cannot update safety' + error_msg = 'Cannot update safely' error_exit(error_msg, exit_code=CLIExitCodes.UNSAFE_UPDATE) logger.info('Node update started') From 62ca678d3bd1ea3b8d91df0c3e455645edde0985 Mon Sep 17 00:00:00 2001 From: badrogger Date: Wed, 9 Oct 2024 15:08:07 +0000 Subject: [PATCH 9/9] Fix typo --- node_cli/cli/node.py | 2 +- node_cli/cli/sync_node.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/node_cli/cli/node.py b/node_cli/cli/node.py index 3c907f42..ff781249 100644 --- a/node_cli/cli/node.py +++ b/node_cli/cli/node.py @@ -112,7 +112,7 @@ def init_node(env_file): @click.option( '--unsafe', 'unsafe_ok', - help='Allow unsafe turn-off', + help='Allow unsafe update', hidden=True, is_flag=True ) diff --git a/node_cli/cli/sync_node.py b/node_cli/cli/sync_node.py index 5a541e3f..a8ad1324 100644 --- a/node_cli/cli/sync_node.py +++ b/node_cli/cli/sync_node.py @@ -77,7 +77,7 @@ def _init_sync(env_file, archive, catchup, historic_state): @click.option( '--unsafe', 'unsafe_ok', - help='Allow unsafe turn-off', + help='Allow unsafe update', hidden=True, is_flag=True )