def test_deploy_regular_with_instance_resume_running(self): """Tests the deploy mechanism in regular mode with instance within resume action for running service.""" instances = {"crypto": {"artifact": "cryptocurrency"}} cryptocurrency_advanced_config_dict = generate_config(self.network, instances=instances) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() self.wait_for_api_restart() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, True) self.assertEqual(len(launcher.launch_state.completed_configs()), 1) # try to resume running service instances = {"crypto": {"artifact": "cryptocurrency", "action": "resume"}} cryptocurrency_advanced_config_dict = generate_config(self.network, instances=instances, artifact_action="none") cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() with self.assertRaises(NotCommittedError): launcher.wait_for_start()
def test_deploy_run_dev(self): """Tests the deploy mechanism in run-dev mode.""" cryptocurrency_advanced_config_dict = { "networks": launcher_networks(self.network), "deadline_height": 10000, "artifacts": { "cryptocurrency": { "runtime": "rust", "name": "exonum-cryptocurrency-advanced:0.13.0-rc.2" } }, # We aren't testing initialization here. "instances": {}, } cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, True)
def test_deploy_regular_invalid_artifact_name(self): """Tests the deploy mechanism in regular mode with invalid artifact""" cryptocurrency_advanced_config_dict = { "networks": launcher_networks(self.network), "deadline_height": 10000, "artifacts": { "cryptocurrency": { "runtime": "rust", "name": "test-service:0.13.0-rc.2" } }, # We aren't testing initialization here. "instances": {}, } cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() # invalid artifact should not be deployed for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, False)
def test_deploy_regular_exceed_deadline_height(self): """Tests the deploy mechanism in regular mode with exceeded deadline height""" cryptocurrency_advanced_config_dict = { "networks": launcher_networks(self.network), "deadline_height": 0, "artifacts": { "cryptocurrency": { "runtime": "rust", "name": "exonum-cryptocurrency-advanced", "version": "0.13.0-rc.2", } }, # We aren't testing initialization here. "instances": {}, } cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() # artifact should not be deployed because of exceeded deadline height for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, False)
def test_deploy_regular_with_instance_stop_action_before_start(self): """Tests the deploy mechanism in regular mode with instance within stop action before start.""" cryptocurrency_advanced_config_dict = { "networks": launcher_networks(self.network), "deadline_height": 10000, "artifacts": { "cryptocurrency": { "runtime": "rust", "name": "exonum-cryptocurrency-advanced", "version": "0.13.0-rc.2", } }, "instances": {"crypto": {"artifact": "cryptocurrency", "action": "stop"}}, } cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict ) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() with self.assertRaises(RuntimeError): launcher.start_all()
def setUp(self): try: self.network = run_4_nodes("exonum-cryptocurrency-advanced") wait_network_to_start(self.network) instances = {"crypto": {"artifact": "cryptocurrency"}} cryptocurrency_advanced_config_dict = generate_config( self.network, instances=instances) cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, True) # Launcher checks that config is applied, no need to check it again. except Exception as error: # If exception is raise in `setUp`, `tearDown` won't be called, # thus here we ensure that network is stopped and temporary data is removed. # Then we re-raise exception, since the test should fail. self.network.stop() self.network.deinitialize() raise error
def test_deinitialize(self) -> None: """Tests that deinitialize deinitializes Supervisor.""" config = TestConfiguration.load_config("sample_config.yml") launcher = Launcher(config) # Create mock OK response. response = Response() response.status_code = 200 # Setup init mocks. launcher._supervisor.initialize = MagicMock( return_value=None) # type: ignore for client in launcher.clients: client.public_api.stats = MagicMock(return_value=response) # Initialize launcher. launcher.initialize() # Setup deinit mock. launcher._supervisor.deinitialize = MagicMock( return_value=None) # type: ignore # Deinitialize launcher.deinitialize() launcher._supervisor.deinitialize.assert_called() # type: ignore
def test_deploy_dev_with_instance(self): """Tests the deploy mechanism in dev mode with instance.""" cryptocurrency_advanced_config_dict = { "networks": launcher_networks(self.network), "deadline_height": 10000, "artifacts": { "cryptocurrency": { "runtime": "rust", "name": "exonum-cryptocurrency-advanced", "version": "0.13.0-rc.2", } }, "instances": {"crypto": {"artifact": "cryptocurrency"}}, } cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict ) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, True) self.assertEqual(len(launcher.launch_state.completed_configs()), 1)
def setUp(self): self.network = run_4_nodes("exonum-cryptocurrency-advanced") time.sleep(3) cryptocurrency_advanced_config_dict = { "networks": launcher_networks(self.network), "deadline_height": 10000, "artifacts": { "cryptocurrency": { "runtime": "rust", "name": "exonum-cryptocurrency-advanced:0.13.0-rc.2" } }, "instances": { "crypto": { "artifact": "cryptocurrency" } }, } cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, True) for instance in launcher.launch_state.completed_initializations(): explorer.wait_for_start(instance)
def test_load_plugins_runtime_only(self) -> None: """Tests that plugins are loaded as expected if only the runtime plugins are present: no artifact plugins""" config = TestConfiguration.load_config( "custom_plugins_runtime_only.yml") launcher = Launcher(config) self.assertEqual(type(launcher._runtime_plugins["rust"]), RustSpecLoader) self.assertEqual(type(launcher._runtime_plugins["sample3"]), TestRuntimeSpecLoader)
def test_unload_artifact_of_running_service(self): """Tests unload logic when running service references to an artifact.""" # Deploy a service with 0.2.0 version. instances = {INSTANCE_NAME: {"artifact": "cryptocurrency"}} config_dict = generate_config(self.network, instances=instances) deploy_config = Configuration(config_dict) with Launcher(deploy_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() self.wait_for_api_restart() explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertTrue(deployed) # Try to unload artifact with version 0.1.0 unload_config_dict = generate_config(self.network, instances=instances, artifact_action="unload", artifact_version="0.1.0") unload_config = Configuration(unload_config_dict) with Launcher(unload_config) as launcher: launcher.unload_all() launcher.wait_for_unload() self.wait_for_api_restart() explorer = launcher.explorer() for artifact in unload_config.artifacts.values(): deployed = explorer.is_deployed(artifact) self.assertTrue(deployed) # Not False !!! status, message = launcher.launch_state.unload_status self.assertEqual(status, ActionResult.Fail) self.assertIn( "service `101:cryptocurrency` references it as the current artifact", message)
def test_deploy_regular_with_consensus_config(self): """Tests the deploy mechanism in regular mode with consensus config.""" pub_configs = self.network._public_configs().split() validator_keys = [] for pub_config in pub_configs: keys = [] with open(pub_config, "r") as file: data = file.read() keys.append(re.search('consensus_key = "(.+?)"', data).group(1)) keys.append(re.search('service_key = "(.+?)"', data).group(1)) validator_keys.append(keys) consensus = { "validator_keys": validator_keys, "first_round_timeout": 3000, "status_timeout": 5000, "peers_timeout": 10000, "txs_block_limit": 5000, "max_message_len": 1048576, "min_propose_timeout": 10, "max_propose_timeout": 200, "propose_timeout_threshold": 500, } instances = {"crypto": {"artifact": "cryptocurrency"}} cryptocurrency_advanced_config_dict = generate_config( self.network, consensus=consensus, instances=instances ) cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict ) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.check_deployed(artifact) self.assertEqual(deployed, True) self.assertEqual(len(launcher.launch_state.completed_configs()), 1) for validator_id in range(self.network.validators_count()): host, public_port, private_port = self.network.api_address(validator_id) client = ExonumClient(host, public_port, private_port) supervisor_api = client.service_apis("supervisor") consensus_config = supervisor_api[0].get_service("consensus-config").json() # check that initial config has been applied self.assertEqual(consensus_config["txs_block_limit"], 5000)
def test_migrate_running_service(self): """Tests migration flow when the migrating service is running.""" # Deploy a service with 0.2.0 version. instances = {INSTANCE_NAME: {"artifact": "cryptocurrency"}} config_dict = generate_config(self.network, instances=instances) deploy_config = Configuration(config_dict) with Launcher(deploy_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() self.wait_for_api_restart() explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertTrue(deployed) # Migrate service data from 0.1.0 to 0.2.0 version migrations = { INSTANCE_NAME: { "runtime": "rust", "name": "exonum-cryptocurrency", "version": "0.2.0" } } migrations_dict = generate_migration_config(self.network, migrations) migration_config = Configuration(migrations_dict) with Launcher(migration_config) as launcher: launcher.migrate_all() launcher.wait_for_migration() for instance, ( status, message ) in launcher.launch_state.completed_migrations().items(): if instance == INSTANCE_NAME: self.assertEqual(status, ActionResult.Fail) self.assertIn("is not stopped or frozen", message)
def test_load_plugins(self) -> None: """Tests that plugins are loaded as expected.""" config = TestConfiguration.load_config("custom_plugins.yml") cryptocurrency = config.artifacts["cryptocurrency"] launcher = Launcher(config) self.assertEqual(type(launcher._runtime_plugins["rust"]), RustSpecLoader) self.assertEqual(type(launcher._runtime_plugins["sample"]), TestRuntimeSpecLoader) self.assertEqual(type(launcher._artifact_plugins[cryptocurrency]), TestInstanceSpecLoader)
def test_deploy_regular_with_instance_resume_action_before_start(self): """Tests the deploy mechanism in regular mode with instance within resume action before start.""" instances = {"crypto": {"artifact": "cryptocurrency", "action": "resume"}} cryptocurrency_advanced_config_dict = generate_config(self.network, instances=instances) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() with self.assertRaises(RuntimeError): launcher.start_all()
def test_freeze_service(self): host, public_port, private_port = self.network.api_address(0) client = ExonumClient(host, public_port, private_port) # Create wallet alice_keys = KeyPair.generate() with ExonumCryptoAdvancedClient(client) as crypto_client: crypto_client.create_wallet(alice_keys, "Alice") with client.create_subscriber("transactions") as subscriber: subscriber.wait_for_new_event() alice_balance = crypto_client.get_balance(alice_keys) self.assertEqual(alice_balance, 100) # Freeze the service instances = { "crypto": { "artifact": "cryptocurrency", "action": "freeze" } } cryptocurrency_advanced_config_dict = generate_config( self.network, instances=instances, artifact_action="none") cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() # Check that the service status has been changed to `frozen`. for service in client.public_api.available_services().json( )["services"]: if service["spec"]["name"] == "crypto": self.assertEqual(service["status"]["type"], "frozen") # Try to create a new wallet. The operation should fail. with ExonumCryptoAdvancedClient(client) as crypto_client: bob_keys = KeyPair.generate() response = crypto_client.create_wallet(bob_keys, "Bob") self.assertEqual(response.status_code, 400) # Because the service is frozen, transaction should be inadmissible. self.assertEqual(response.json()["title"], "Failed to add transaction to memory pool") # Check that we can use service endpoints for data retrieving. Check wallet once again. with ExonumCryptoAdvancedClient(client) as crypto_client: alice_balance = crypto_client.get_balance(alice_keys) self.assertEqual(alice_balance, 100)
def test_creation(self) -> None: """Tests the creation of the launcher""" config = TestConfiguration.load_config("sample_config.yml") launcher = Launcher(config) self.assertEqual(len(launcher.clients), len(config.networks)) for i, network in enumerate(config.networks): self.assertEqual(launcher.clients[i].hostname, network["host"]) self.assertEqual(launcher.clients[i].public_api_port, network["public-api-port"]) self.assertEqual(launcher.clients[i].private_api_port, network["private-api-port"]) schema = "https" if network["ssl"] else "http" self.assertEqual(launcher.clients[i].schema, schema)
def test_deploy_regular_without_instance(self): """Tests the deploy mechanism in regular mode without instance""" cryptocurrency_advanced_config_dict = generate_config(self.network) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, True)
def test_deploy_regular_exceed_deadline_height(self): """Tests the deploy mechanism in regular mode with exceeded deadline height""" cryptocurrency_advanced_config_dict = generate_config(self.network, deadline_height=0) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() with self.assertRaises(NotCommittedError): launcher.wait_for_deploy() # artifact should not be deployed because of exceeded deadline height explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, False)
def test_deploy_run_dev(self): """Tests the deploy mechanism in run-dev mode.""" cryptocurrency_advanced_config_dict = generate_config(self.network) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertTrue(deployed)
def test_deploy_regular_invalid_artifact_name(self): """Tests the deploy mechanism in regular mode with invalid artifact""" cryptocurrency_advanced_config_dict = generate_config(self.network, artifact_name="test-artifact") cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() # invalid artifact should not be deployed explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, False)
def test_deploy_regular_exceed_deadline_height(self): """Tests the deploy mechanism in regular mode with exceeded deadline height""" cryptocurrency_advanced_config_dict = generate_config(self.network, deadline_height=0) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() # artifact should not be deployed because of exceeded deadline height explorer = launcher.explorer() for artifact, (action_result, message) in launcher.launch_state.completed_deployments().items(): deployed = explorer.is_deployed(artifact) self.assertFalse(deployed) self.assertEqual(action_result, ActionResult.Fail) self.assertIn("Actual height for transaction is in the past", message)
def setUp(self): self.network = run_4_nodes("exonum-cryptocurrency-advanced") self.addCleanup(self._tear_down, False) wait_network_to_start(self.network) instances = {"crypto": {"artifact": "cryptocurrency"}} cryptocurrency_advanced_config_dict = generate_config( self.network, instances=instances) cryptocurrency_advanced_config = Configuration( cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, True)
def test_deploy_regular_with_invalid_instance(self): """Tests the deploy mechanism in regular mode with invalid instance.""" instances = {"": {"artifact": "cryptocurrency"}} cryptocurrency_advanced_config_dict = generate_config(self.network, instances=instances) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() with self.assertRaises(NotCommittedError): launcher.wait_for_start() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, True)
def test_deploy_dev_with_instance(self): """Tests the deploy mechanism in dev mode with instance.""" instances = {"crypto": {"artifact": "cryptocurrency"}} cryptocurrency_advanced_config_dict = generate_config(self.network, instances=instances) cryptocurrency_advanced_config = Configuration(cryptocurrency_advanced_config_dict) with Launcher(cryptocurrency_advanced_config) as launcher: explorer = launcher.explorer() launcher.deploy_all() launcher.wait_for_deploy() launcher.start_all() launcher.wait_for_start() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertEqual(deployed, True) self.assertEqual(len(launcher.launch_state.completed_configs()), 1)
def test_deploy_all(self) -> None: """Tests that deploy method uses supervisor to deploy all artifacts from config.""" config = TestConfiguration.load_config("sample_config.yml") launcher = Launcher(config) # Build a list of expected arguements for method calls. create_calls_sequence = [] send_calls_sequence = [] for artifact in config.artifacts.values(): # Skip artifacts that should not be deployed if not artifact.deploy: continue create_calls_sequence.append( call(artifact, launcher._runtime_plugins[artifact.runtime])) send_calls_sequence.append(call(b"123")) # Mock methods. launcher._supervisor.create_deploy_request = MagicMock( return_value=b"123") # type: ignore launcher._supervisor.send_deploy_request = MagicMock( return_value=["123"]) # type: ignore # Call deploy. launcher.deploy_all() # Check that methods were invoked with the expected arguments and in the expected order. launcher._supervisor.create_deploy_request.assert_has_calls( create_calls_sequence) # type: ignore launcher._supervisor.send_deploy_request.assert_has_calls( send_calls_sequence) # type: ignore # Check that results were added to the pending deployments. for artifact in config.artifacts.values(): if artifact.deploy: self.assertEqual( launcher.launch_state._pending_deployments[artifact], ["123"]) else: self.assertTrue( artifact not in launcher.launch_state._pending_deployments)
def test_start_all(self) -> None: """Tests that deploy method uses supervisor to deploy all artifacts from config.""" config = TestConfiguration.load_config("sample_config.yml") launcher = Launcher(config) # Mark all the artifacts as successfully deployed. for artifact in config.artifacts.values(): launcher.launch_state._completed_deployments[ artifact] = ActionResult.Success # Build a list of expected arguements for method calls. start_calls_sequence = [] send_calls_sequence = [] spec_loaders = [ launcher._artifact_plugins.get(instance.artifact, MockDefaultInstanceSpecLoader()) for instance in config.instances ] start_calls_sequence.append( call(None, config.instances, spec_loaders, config.actual_from)) send_calls_sequence.append(call(b"123")) # Mock methods. launcher._supervisor.create_config_change_request = MagicMock( return_value=b"123") # type: ignore launcher._supervisor.send_propose_config_request = MagicMock( return_value=["123"]) # type: ignore # Call start. launcher.start_all() # Check that methods were invoked with the expected arguments and in the expected order. launcher._supervisor.create_config_change_request.assert_has_calls( start_calls_sequence) # type: ignore launcher._supervisor.send_propose_config_request.assert_has_calls( send_calls_sequence) # type: ignore # Check that results were added to the pending configs. self.assertEqual( launcher.launch_state._pending_configs[launcher.config], ["123"])
def test_initialize(self) -> None: """Tests that on initialize launcher initializes Supervisor and verifies clients.""" config = TestConfiguration.load_config("sample_config.yml") launcher = Launcher(config) # Create mock OK response. response = Response() response.status_code = 200 # Setup mocks. launcher._supervisor.initialize = MagicMock( return_value=None) # type: ignore for client in launcher.clients: client.public_api.stats = MagicMock(return_value=response) # Initialize launcher. launcher.initialize() # Check that expected methods are called launcher._supervisor.initialize.assert_called() # type: ignore for client in launcher.clients: client.public_api.stats.assert_called()
def test_context_manager(self) -> None: """Tests that creation via `with` calls initialize and deinitialize""" config = TestConfiguration.load_config("sample_config.yml") old_init = Launcher.initialize old_deinit = Launcher.deinitialize # Setup mocks. Launcher.initialize = MagicMock(return_value=None) # type: ignore Launcher.deinitialize = MagicMock(return_value=None) # type: ignore # Create launcher. with Launcher(config) as _: pass # Check that everything was called. # pylint: disable=no-member Launcher.initialize.assert_called() # type: ignore Launcher.deinitialize.assert_called() # type: ignore # Restore methods. Launcher.initialize = old_init # type: ignore Launcher.deinitialize = old_deinit # type: ignore
def full_migration_flow(self, action: str): host, public_port, private_port = self.network.api_address(0) client = ExonumClient(host, public_port, private_port) # Deploy a service with 0.2.0 version. instances = {INSTANCE_NAME: {"artifact": "cryptocurrency"}} config_dict = generate_config(self.network, instances=instances) deploy_config = Configuration(config_dict) with Launcher(deploy_config) as launcher: launcher.deploy_all() launcher.wait_for_deploy() self.wait_for_api_restart() explorer = launcher.explorer() for artifact in launcher.launch_state.completed_deployments(): deployed = explorer.is_deployed(artifact) self.assertTrue(deployed) # Create Alice's wallet with 0.1.0 version of the service alice_keys = self._create_wallet(client, "Alice", "0.1.0") # Stop the working service with version 0.1.0. instances = { INSTANCE_NAME: { "artifact": "cryptocurrency", "action": action } } stop_config_dict = generate_config(self.network, instances=instances, artifact_action="none", artifact_version="0.1.0") stop_config = Configuration(stop_config_dict) with Launcher(stop_config) as launcher: launcher.start_all() launcher.wait_for_start() self.wait_for_api_restart() # Check that the service status has been changed to `stopped`. for service in client.public_api.available_services().json( )["services"]: if service["spec"]["name"] == INSTANCE_NAME: self.assertEqual( service["status"]["type"], "stopped" if action == "stop" else "frozen") # Migrate service data from 0.1.0 to 0.2.0 version migrations = { INSTANCE_NAME: { "runtime": "rust", "name": "exonum-cryptocurrency", "version": "0.2.0" } } migrations_dict = generate_migration_config(self.network, migrations) migration_config = Configuration(migrations_dict) with Launcher(migration_config) as launcher: launcher.migrate_all() launcher.wait_for_migration() for service in client.public_api.available_services().json( )["services"]: if service["spec"]["name"] == INSTANCE_NAME: self.assertEqual(service["data_version"], "0.2.0") # Switch service artifact from 0.1.0 to 0.2.0 version with Launcher(migration_config) as launcher: launcher.migrate_all() launcher.wait_for_migration() for service in client.public_api.available_services().json( )["services"]: if service["spec"]["name"] == INSTANCE_NAME: self.assertEqual(service["spec"]["artifact"]["version"], "0.2.0") # Resume service with a new logic version 0.2.0 instances = { INSTANCE_NAME: { "artifact": "cryptocurrency", "action": "resume" } } resume_config_dict = generate_config(self.network, instances=instances, artifact_action="none") resume_config = Configuration(resume_config_dict) with Launcher(resume_config) as launcher: launcher.start_all() launcher.wait_for_start() self.wait_for_api_restart() # Check that the service status has been changed to `active`. for service in client.public_api.available_services().json( )["services"]: if service["spec"]["name"] == INSTANCE_NAME: self.assertEqual(service["status"]["type"], "active") self.assertEqual(service["spec"]["artifact"]["version"], "0.2.0") # Unload artifact with version 0.1.0 unload_config_dict = generate_config(self.network, instances=instances, artifact_action="unload", artifact_version="0.1.0") unload_config = Configuration(unload_config_dict) with Launcher(unload_config) as launcher: launcher.unload_all() launcher.wait_for_unload() self.wait_for_api_restart() explorer = launcher.explorer() for artifact in unload_config.artifacts.values(): deployed = explorer.is_deployed(artifact) self.assertFalse(deployed) # Create Bob's wallet with version 0.2.0 of the service. bob_keys = self._create_wallet(client, "Bob", "0.2.0") # Transfer some coins and check balances and history length. with ExonumCryptoAdvancedClient( client, instance_name=INSTANCE_NAME) as crypto_client: alice_balance = crypto_client.get_balance(alice_keys) self.assertEqual(alice_balance, 100) alice_history_len = crypto_client.get_history_len(alice_keys) self.assertEqual(alice_history_len, 0) bob_balance = crypto_client.get_balance(bob_keys) self.assertEqual(bob_balance, 100) crypto_client.transfer(20, alice_keys, bob_keys.public_key) with client.create_subscriber("transactions") as subscriber: subscriber.wait_for_new_event() alice_balance = crypto_client.get_balance(alice_keys) self.assertEqual(alice_balance, 80) # Get a value from the new field `history_len`. alice_history_len = crypto_client.get_history_len(alice_keys) self.assertEqual(alice_history_len, 1) bob_balance = crypto_client.get_balance(bob_keys) self.assertEqual(bob_balance, 120)