From aa008c63d1d0bff3f1f9f669060c9fc4edcfa7ac Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:54:34 +0200 Subject: [PATCH 01/17] protected --- .../simcore_service_autoscaling/modules/auto_scaling_core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index b1e629f4e7a..1b9c62c76f8 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -327,7 +327,7 @@ async def _try_attach_pending_ec2s( ) -async def sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: +async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: app_settings: ApplicationSettings = app.state.settings assert app_settings.AUTOSCALING_EC2_INSTANCES # nosec ec2_client = get_ec2_client(app) @@ -1217,8 +1217,7 @@ async def auto_scale_cluster( If there are such tasks, this method will allocate new machines in AWS to cope with the additional load. """ - - allowed_instance_types = await sorted_allowed_instance_types(app) + allowed_instance_types = await _sorted_allowed_instance_types(app) cluster = await _analyze_current_cluster( app, auto_scaling_mode, allowed_instance_types ) From c05eeae9ac3fb08f3ac95615e014725c61b1ef2d Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:54:49 +0200 Subject: [PATCH 02/17] sorted --- packages/aws-library/src/aws_library/ec2/_client.py | 8 ++++---- packages/aws-library/tests/test_ec2_client.py | 7 +------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/aws-library/src/aws_library/ec2/_client.py b/packages/aws-library/src/aws_library/ec2/_client.py index 14094939dde..7ce24461b0c 100644 --- a/packages/aws-library/src/aws_library/ec2/_client.py +++ b/packages/aws-library/src/aws_library/ec2/_client.py @@ -68,10 +68,10 @@ async def get_ec2_instance_capabilities( self, instance_type_names: set[InstanceTypeType], ) -> list[EC2InstanceType]: - """returns the ec2 instance types from a list of instance type names - NOTE: the order might differ! + """Returns the ec2 instance types from a list of instance type names (sorted by name) + Arguments: - instance_type_names -- the types to filter with + instance_type_names -- the types to filter with. If an empty set, it returns all. Raises: Ec2InstanceTypeInvalidError: some invalid types were used as filter @@ -95,7 +95,7 @@ async def get_ec2_instance_capabilities( ), ) ) - return list_instances + return sorted(list_instances, key=lambda i: i.name) @ec2_exception_handler(_logger) async def launch_instances( diff --git a/packages/aws-library/tests/test_ec2_client.py b/packages/aws-library/tests/test_ec2_client.py index 625555e9f5d..5eb8017898a 100644 --- a/packages/aws-library/tests/test_ec2_client.py +++ b/packages/aws-library/tests/test_ec2_client.py @@ -116,12 +116,7 @@ async def test_get_ec2_instance_capabilities( ) ) assert instance_types - assert len(instance_types) == len(ec2_allowed_instances) - - # all the instance names are found and valid - assert all(i.name in ec2_allowed_instances for i in instance_types) - for instance_type_name in ec2_allowed_instances: - assert any(i.name == instance_type_name for i in instance_types) + assert [_.name for _ in instance_types] == sorted(ec2_allowed_instances) async def test_get_ec2_instance_capabilities_empty_list_returns_all_options( From c489ad2703f84b7cc7123b271e35d5ba756f7d08 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:56:05 +0200 Subject: [PATCH 03/17] doc --- .../modules/auto_scaling_core.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index 1b9c62c76f8..e023461b421 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -1217,10 +1217,13 @@ async def auto_scale_cluster( If there are such tasks, this method will allocate new machines in AWS to cope with the additional load. """ + # current state allowed_instance_types = await _sorted_allowed_instance_types(app) cluster = await _analyze_current_cluster( app, auto_scaling_mode, allowed_instance_types ) + + # cleanup cluster = await _cleanup_disconnected_nodes(app, cluster) cluster = await _terminate_broken_ec2s(app, cluster) cluster = await _make_pending_buffer_ec2s_join_cluster(app, cluster) @@ -1229,8 +1232,11 @@ async def auto_scale_cluster( ) cluster = await _drain_retired_nodes(app, cluster) + # desired state cluster = await _autoscale_cluster( app, cluster, auto_scaling_mode, allowed_instance_types ) + + # notify await _notify_machine_creation_progress(app, cluster, auto_scaling_mode) await _notify_autoscaling_status(app, cluster, auto_scaling_mode) From 69b9ad9bca3f6c55d07a81711ed0a067205fc8b6 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:26:21 +0200 Subject: [PATCH 04/17] warns if all --- .../core/settings.py | 3 +- .../modules/auto_scaling_core.py | 34 ++++++++----- .../unit/test_modules_auto_scaling_core.py | 51 +++++++++++++++++++ 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 services/autoscaling/tests/unit/test_modules_auto_scaling_core.py diff --git a/services/autoscaling/src/simcore_service_autoscaling/core/settings.py b/services/autoscaling/src/simcore_service_autoscaling/core/settings.py index 23af4b958bf..bc82ecbbd01 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/core/settings.py +++ b/services/autoscaling/src/simcore_service_autoscaling/core/settings.py @@ -60,7 +60,8 @@ class Config(EC2Settings.Config): class EC2InstancesSettings(BaseCustomSettings): EC2_INSTANCES_ALLOWED_TYPES: dict[str, EC2InstanceBootSpecific] = Field( ..., - description="Defines which EC2 instances are considered as candidates for new EC2 instance and their respective boot specific parameters", + description="Defines which EC2 instances are considered as candidates for new EC2 instance and their respective boot specific parameters" + "WARNING: if empty, all available ec2 instances are allowed", ) EC2_INSTANCES_KEY_NAME: str = Field( diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index e023461b421..4d5d9257101 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -4,6 +4,7 @@ import datetime import itertools import logging +import typing from typing import Final, cast import arrow @@ -333,24 +334,31 @@ async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: ec2_client = get_ec2_client(app) # some instances might be able to run several tasks + selected_names = list( + app_settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES + ) + assert set(selected_names).issubset(typing.get_args(InstanceTypeType)) # nosec + allowed_instance_types: list[ EC2InstanceType ] = await ec2_client.get_ec2_instance_capabilities( - cast( - set[InstanceTypeType], - set( - app_settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES, - ), - ) + cast(set[InstanceTypeType], set(selected_names)) ) - def _sort_according_to_allowed_types(instance_type: EC2InstanceType) -> int: - assert app_settings.AUTOSCALING_EC2_INSTANCES # nosec - return list( - app_settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES - ).index(f"{instance_type.name}") + if selected_names: - allowed_instance_types.sort(key=_sort_according_to_allowed_types) + def _as_selection(instance_type: EC2InstanceType) -> int: + assert app_settings.AUTOSCALING_EC2_INSTANCES # nosec + return selected_names.index(f"{instance_type.name}") + + allowed_instance_types.sort(key=_as_selection) + else: + # NOTE An empty set to get_ec2_instance_capabilities it will return ALL of the instances + _logger.warning( + "All %s instances are allowed since EC2_INSTANCES_ALLOWED_TYPES is set to empty (=%s)", + len(allowed_instance_types), + selected_names, + ) return allowed_instance_types @@ -1131,7 +1139,7 @@ async def _autoscale_cluster( # 1. check if we have pending tasks and resolve them by activating some drained nodes unrunnable_tasks = await auto_scaling_mode.list_unrunnable_tasks(app) _logger.info("found %s unrunnable tasks", len(unrunnable_tasks)) - + # NOTE: this function predicts how dask will assign a task to a machine queued_or_missing_instance_tasks, cluster = await _assign_tasks_to_current_cluster( app, unrunnable_tasks, cluster, auto_scaling_mode ) diff --git a/services/autoscaling/tests/unit/test_modules_auto_scaling_core.py b/services/autoscaling/tests/unit/test_modules_auto_scaling_core.py new file mode 100644 index 00000000000..aca17290ee0 --- /dev/null +++ b/services/autoscaling/tests/unit/test_modules_auto_scaling_core.py @@ -0,0 +1,51 @@ +# pylint: disable=redefined-outer-name +# pylint: disable=unused-argument +# pylint: disable=unused-variable +# pylint: disable=too-many-arguments + + +import logging + +import pytest +from fastapi import FastAPI +from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict +from simcore_service_autoscaling.modules.auto_scaling_core import ( + _sorted_allowed_instance_types, +) + + +@pytest.fixture +def with_empty_ec2_intances_allowed_types( + monkeypatch: pytest.MonkeyPatch, + app_environment: EnvVarsDict, + disabled_rabbitmq: None, + mocked_ec2_server_envs: EnvVarsDict, + mocked_ssm_server_envs: EnvVarsDict, + mocked_redis_server: None, +) -> EnvVarsDict: + return app_environment | setenvs_from_dict( + monkeypatch, + { + "EC2_INSTANCES_ALLOWED_TYPES": "{}", + }, + ) + + +async def test_sorted_allowed_instance_types__warns_with_all_available( + with_empty_ec2_intances_allowed_types: EnvVarsDict, + initialized_app: FastAPI, + caplog: pytest.LogCaptureFixture, +): + app = initialized_app + + selected_names = list( + app.state.settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES + ) + assert not selected_names + + with caplog.at_level(logging.WARNING): + allowed = await _sorted_allowed_instance_types(app) + assert allowed + assert len(allowed) > 100 + + assert len(caplog.records) == 1 From 40c7072cd79fffbad6b584127ecebefd49f2d68a Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:51:22 +0200 Subject: [PATCH 05/17] refactors _assign_tasks_to_current_cluster --- .../modules/auto_scaling_core.py | 66 +++++++++---------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index 4d5d9257101..88bb2b93fe4 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -2,6 +2,7 @@ import collections import dataclasses import datetime +import functools import itertools import logging import typing @@ -505,51 +506,44 @@ async def _assign_tasks_to_current_cluster( cluster: Cluster, auto_scaling_mode: BaseAutoscaling, ) -> tuple[list, Cluster]: + """Estimates how tasks will be assigned to cluster's instances + based on the resources required by each task + + Returns: + A tuple with + - list of unassigned tasks (i.e. those not fitting available machines in cluster) + - same cluster instance as in the input + + """ unassigned_tasks = [] + assignment_functions = [ + functools.partial(_try_assign_task_to_ec2_instance, instances=instances) + for instances in ( + cluster.active_nodes, + cluster.drained_nodes + cluster.buffer_drained_nodes, + cluster.pending_nodes, + cluster.pending_ec2s, + cluster.buffer_ec2s, + ) + ] + for task in tasks: task_required_resources = auto_scaling_mode.get_task_required_resources(task) task_required_ec2_instance = await auto_scaling_mode.get_task_defined_instance( app, task ) - assignment_functions = [ - lambda task, required_ec2, required_resources: _try_assign_task_to_ec2_instance( - task, - instances=cluster.active_nodes, - task_required_ec2_instance=required_ec2, - task_required_resources=required_resources, - ), - lambda task, required_ec2, required_resources: _try_assign_task_to_ec2_instance( - task, - instances=cluster.drained_nodes + cluster.buffer_drained_nodes, - task_required_ec2_instance=required_ec2, - task_required_resources=required_resources, - ), - lambda task, required_ec2, required_resources: _try_assign_task_to_ec2_instance( - task, - instances=cluster.pending_nodes, - task_required_ec2_instance=required_ec2, - task_required_resources=required_resources, - ), - lambda task, required_ec2, required_resources: _try_assign_task_to_ec2_instance( - task, - instances=cluster.pending_ec2s, - task_required_ec2_instance=required_ec2, - task_required_resources=required_resources, - ), - lambda task, required_ec2, required_resources: _try_assign_task_to_ec2_instance( - task, - instances=cluster.buffer_ec2s, - task_required_ec2_instance=required_ec2, - task_required_resources=required_resources, - ), - ] - if any( - assignment(task, task_required_ec2_instance, task_required_resources) - for assignment in assignment_functions + is_assigned( + task, + task_required_ec2_instance=task_required_ec2_instance, + task_required_resources=task_required_resources, + ) + for is_assigned in assignment_functions ): - _logger.debug("assigned task to cluster") + _logger.debug( + "task %s is assigned to one instance available in cluster", task + ) else: unassigned_tasks.append(task) From 4de5aa11735387f55f81cb66676cb58ea31dd128 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:52:13 +0200 Subject: [PATCH 06/17] cleanup --- .../modules/auto_scaling_core.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index 88bb2b93fe4..814e57d3910 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -506,17 +506,17 @@ async def _assign_tasks_to_current_cluster( cluster: Cluster, auto_scaling_mode: BaseAutoscaling, ) -> tuple[list, Cluster]: - """Estimates how tasks will be assigned to cluster's instances - based on the resources required by each task + """ + Evaluates whether a task can be executed on any instance within the cluster. If the task's resource requirements are met, the task is *denoted* as assigned to the cluster. + Note: This is an estimation only since actual scheduling is handled by Dask. Returns: - A tuple with - - list of unassigned tasks (i.e. those not fitting available machines in cluster) - - same cluster instance as in the input - + A tuple containing: + - A list of unassigned tasks (tasks whose resource requirements cannot be fulfilled by the available machines in the cluster). + - The same cluster instance passed as input. """ unassigned_tasks = [] - assignment_functions = [ + assignment_predicates = [ functools.partial(_try_assign_task_to_ec2_instance, instances=instances) for instances in ( cluster.active_nodes, @@ -539,7 +539,7 @@ async def _assign_tasks_to_current_cluster( task_required_ec2_instance=task_required_ec2_instance, task_required_resources=task_required_resources, ) - for is_assigned in assignment_functions + for is_assigned in assignment_predicates ): _logger.debug( "task %s is assigned to one instance available in cluster", task From 5cb7fefea15286cbf8cd0937101106743892faca Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:17:19 +0200 Subject: [PATCH 07/17] @sanderegg review: rename vars --- .../modules/auto_scaling_core.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index 814e57d3910..eac1f9c2fda 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -335,22 +335,24 @@ async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: ec2_client = get_ec2_client(app) # some instances might be able to run several tasks - selected_names = list( + allowed_instance_type_names = list( app_settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES ) - assert set(selected_names).issubset(typing.get_args(InstanceTypeType)) # nosec + assert set(allowed_instance_type_names).issubset( + typing.get_args(InstanceTypeType) + ) # nosec allowed_instance_types: list[ EC2InstanceType ] = await ec2_client.get_ec2_instance_capabilities( - cast(set[InstanceTypeType], set(selected_names)) + cast(set[InstanceTypeType], set(allowed_instance_type_names)) ) - if selected_names: + if allowed_instance_type_names: def _as_selection(instance_type: EC2InstanceType) -> int: assert app_settings.AUTOSCALING_EC2_INSTANCES # nosec - return selected_names.index(f"{instance_type.name}") + return allowed_instance_type_names.index(f"{instance_type.name}") allowed_instance_types.sort(key=_as_selection) else: @@ -358,7 +360,7 @@ def _as_selection(instance_type: EC2InstanceType) -> int: _logger.warning( "All %s instances are allowed since EC2_INSTANCES_ALLOWED_TYPES is set to empty (=%s)", len(allowed_instance_types), - selected_names, + allowed_instance_type_names, ) return allowed_instance_types From b46439450092fbca1a2db6fc326c15e5a3c3bc42 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:19:49 +0200 Subject: [PATCH 08/17] @sanderegg review: rm assert --- .../src/simcore_service_autoscaling/modules/auto_scaling_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index eac1f9c2fda..41414e6c935 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -351,7 +351,6 @@ async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: if allowed_instance_type_names: def _as_selection(instance_type: EC2InstanceType) -> int: - assert app_settings.AUTOSCALING_EC2_INSTANCES # nosec return allowed_instance_type_names.index(f"{instance_type.name}") allowed_instance_types.sort(key=_as_selection) From 2a9de4c99137ff659cce7bb02333402a1299d7ad Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:41:51 +0200 Subject: [PATCH 09/17] cleanup .cookiecutterrc --- .../pytest_plugin/folder_structure.py | 15 -------------- services/autoscaling/.cookiecutterrc | 20 ------------------- services/datcore-adapter/.cookiecutterrc | 19 ------------------ 3 files changed, 54 deletions(-) delete mode 100644 services/autoscaling/.cookiecutterrc delete mode 100644 services/datcore-adapter/.cookiecutterrc diff --git a/packages/service-integration/src/service_integration/pytest_plugin/folder_structure.py b/packages/service-integration/src/service_integration/pytest_plugin/folder_structure.py index 47969490661..ef87dbb5ceb 100644 --- a/packages/service-integration/src/service_integration/pytest_plugin/folder_structure.py +++ b/packages/service-integration/src/service_integration/pytest_plugin/folder_structure.py @@ -46,21 +46,6 @@ def metadata_file(project_slug_dir: Path, request: pytest.FixtureRequest) -> Pat return metadata_file -def get_expected_files(docker_name: str) -> tuple[str, ...]: - return ( - ".cookiecutterrc", - ".dockerignore", - "metadata:metadata.yml", - f"docker/{docker_name}:entrypoint.sh", - f"docker/{docker_name}:Dockerfile", - "service.cli:execute.sh", - "docker-compose-build.yml", - "docker-compose-meta.yml", - "docker-compose.devel.yml", - "docker-compose.yml", - ) - - def assert_path_in_repo(expected_path: str, project_slug_dir: Path): if ":" in expected_path: diff --git a/services/autoscaling/.cookiecutterrc b/services/autoscaling/.cookiecutterrc deleted file mode 100644 index 5ce0be7364d..00000000000 --- a/services/autoscaling/.cookiecutterrc +++ /dev/null @@ -1,20 +0,0 @@ -# This file exists so you can easily regenerate your project. -# -# cookiecutter --overwrite-if-exists --config-file=.cookiecutterrc /home/crespo/devp/cookiecutter-simcore-py-fastapi -# - -default_context: - - _extensions: ['jinja2_time.TimeExtension'] - _output_dir: '/home/crespo/devp/cookiecutter-simcore-py-fastapi/.output/osparc-simcore/services' - _template: '/home/crespo/devp/cookiecutter-simcore-py-fastapi' - detailed_doc: 'n' - distribution_name: 'simcore-service-autoscaling' - full_name: 'Pedro Crespo-Valero' - github_username: 'pcrespov' - package_name: 'simcore_service_autoscaling' - project_name: 'Auto scaling service' - project_short_description: 'Service to auto-scale swarm' - project_slug: 'autoscaling' - version: '0.1.0-alpha' - year: '2022' diff --git a/services/datcore-adapter/.cookiecutterrc b/services/datcore-adapter/.cookiecutterrc deleted file mode 100644 index bee7aaa59cf..00000000000 --- a/services/datcore-adapter/.cookiecutterrc +++ /dev/null @@ -1,19 +0,0 @@ -# This file exists so you can easily regenerate your project. -# -# cookiecutter --overwrite-if-exists --config-file=.cookiecutterrc /home/anderegg/dev/github/cookiecutter-simcore-py-fastapi -# - -default_context: - - _extensions: ['jinja2_time.TimeExtension'] - _template: '/home/anderegg/dev/github/cookiecutter-simcore-py-fastapi' - detailed_doc: 'n' - distribution_name: 'simcore-service-datcore-adapter' - full_name: 'Sylvain Anderegg' - github_username: 'sanderegg' - package_name: 'simcore_service_datcore_adapter' - project_name: 'datcore-adapter' - project_short_description: 'Interfaces with datcore storage' - project_slug: 'datcore-adapter' - version: '0.1.0-alpha' - year: '2021' From ab7604905be10df00d663582f568e4c1347558b8 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:51:15 +0200 Subject: [PATCH 10/17] restricts nonzero items --- .../modules/auto_scaling_core.py | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index 41414e6c935..a301ac0e896 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -5,7 +5,6 @@ import functools import itertools import logging -import typing from typing import Final, cast import arrow @@ -338,9 +337,10 @@ async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: allowed_instance_type_names = list( app_settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES ) - assert set(allowed_instance_type_names).issubset( - typing.get_args(InstanceTypeType) - ) # nosec + + assert ( + allowed_instance_type_names + ), "EC2_INSTANCES_ALLOWED_TYPES cannot be empty!" # nosec allowed_instance_types: list[ EC2InstanceType @@ -348,19 +348,11 @@ async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: cast(set[InstanceTypeType], set(allowed_instance_type_names)) ) - if allowed_instance_type_names: - - def _as_selection(instance_type: EC2InstanceType) -> int: - return allowed_instance_type_names.index(f"{instance_type.name}") + def _as_selection(instance_type: EC2InstanceType) -> int: + # NOTE: will raise ValueError if allowed_instance_types not in allowed_instance_type_names + return allowed_instance_type_names.index(f"{instance_type.name}") - allowed_instance_types.sort(key=_as_selection) - else: - # NOTE An empty set to get_ec2_instance_capabilities it will return ALL of the instances - _logger.warning( - "All %s instances are allowed since EC2_INSTANCES_ALLOWED_TYPES is set to empty (=%s)", - len(allowed_instance_types), - allowed_instance_type_names, - ) + allowed_instance_types.sort(key=_as_selection) return allowed_instance_types From 6e2c2c0206b580df7ba2548fb82d5bbf02f108fb Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:17:41 +0200 Subject: [PATCH 11/17] adds test --- .../core/settings.py | 18 ++++++++----- .../tests/unit/test_core_settings.py | 27 +++++++++++++++---- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/core/settings.py b/services/autoscaling/src/simcore_service_autoscaling/core/settings.py index bc82ecbbd01..78cde19b50e 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/core/settings.py +++ b/services/autoscaling/src/simcore_service_autoscaling/core/settings.py @@ -61,7 +61,7 @@ class EC2InstancesSettings(BaseCustomSettings): EC2_INSTANCES_ALLOWED_TYPES: dict[str, EC2InstanceBootSpecific] = Field( ..., description="Defines which EC2 instances are considered as candidates for new EC2 instance and their respective boot specific parameters" - "WARNING: if empty, all available ec2 instances are allowed", + "NOTE: minimum length >0", ) EC2_INSTANCES_KEY_NAME: str = Field( @@ -134,7 +134,7 @@ class EC2InstancesSettings(BaseCustomSettings): @validator("EC2_INSTANCES_TIME_BEFORE_DRAINING") @classmethod - def ensure_draining_delay_time_is_in_range( + def _ensure_draining_delay_time_is_in_range( cls, value: datetime.timedelta ) -> datetime.timedelta: if value < datetime.timedelta(seconds=10): @@ -145,7 +145,7 @@ def ensure_draining_delay_time_is_in_range( @validator("EC2_INSTANCES_TIME_BEFORE_TERMINATION") @classmethod - def ensure_termination_delay_time_is_in_range( + def _ensure_termination_delay_time_is_in_range( cls, value: datetime.timedelta ) -> datetime.timedelta: if value < datetime.timedelta(minutes=0): @@ -156,12 +156,18 @@ def ensure_termination_delay_time_is_in_range( @validator("EC2_INSTANCES_ALLOWED_TYPES") @classmethod - def check_valid_instance_names( + def _check_valid_instance_names_and_not_empty( cls, value: dict[str, EC2InstanceBootSpecific] ) -> dict[str, EC2InstanceBootSpecific]: # NOTE: needed because of a flaw in BaseCustomSettings # issubclass raises TypeError if used on Aliases parse_obj_as(list[InstanceTypeType], list(value)) + + if not value: + # NOTE: Field( ... , min_items=...) cannot be used to contraint number of iterms in a dict + msg = "At least one item expecte EC2_INSTANCES_ALLOWED_TYPES, got none" + raise ValueError(msg) + return value @@ -294,12 +300,12 @@ def LOG_LEVEL(self): # noqa: N802 @validator("AUTOSCALING_LOGLEVEL") @classmethod - def valid_log_level(cls, value: str) -> str: + def _valid_log_level(cls, value: str) -> str: return cls.validate_log_level(value) @root_validator() @classmethod - def exclude_both_dynamic_computational_mode(cls, values): + def _exclude_both_dynamic_computational_mode(cls, values): if ( values.get("AUTOSCALING_DASK") is not None and values.get("AUTOSCALING_NODES_MONITORING") is not None diff --git a/services/autoscaling/tests/unit/test_core_settings.py b/services/autoscaling/tests/unit/test_core_settings.py index 7b99bb6bf5a..9315c8fcfd1 100644 --- a/services/autoscaling/tests/unit/test_core_settings.py +++ b/services/autoscaling/tests/unit/test_core_settings.py @@ -132,12 +132,16 @@ def test_invalid_EC2_INSTANCES_TIME_BEFORE_TERMINATION( # noqa: N802 ) -def test_EC2_INSTANCES_ALLOWED_TYPES( # noqa: N802 +def test_EC2_INSTANCES_ALLOWED_TYPES_valid( # noqa: N802 app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch, faker: Faker ): settings = ApplicationSettings.create_from_envs() assert settings.AUTOSCALING_EC2_INSTANCES + +def test_EC2_INSTANCES_ALLOWED_TYPES_passing_invalid_image_tags( # noqa: N802 + app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch, faker: Faker +): # passing an invalid image tag name will fail setenvs_from_dict( monkeypatch, @@ -155,6 +159,10 @@ def test_EC2_INSTANCES_ALLOWED_TYPES( # noqa: N802 with pytest.raises(ValidationError): ApplicationSettings.create_from_envs() + +def test_EC2_INSTANCES_ALLOWED_TYPES_passing_valid_image_tags( # noqa: N802 + app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch, faker: Faker +): # passing a valid will pass setenvs_from_dict( monkeypatch, @@ -176,14 +184,23 @@ def test_EC2_INSTANCES_ALLOWED_TYPES( # noqa: N802 ) settings = ApplicationSettings.create_from_envs() assert settings.AUTOSCALING_EC2_INSTANCES - assert [ + assert next( + iter(settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES.values()) + ).pre_pull_images == [ "nginx:latest", "itisfoundation/my-very-nice-service:latest", "simcore/services/dynamic/another-nice-one:2.4.5", "asd", - ] == next( - iter(settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES.values()) - ).pre_pull_images + ] + + +def test_EC2_INSTANCES_ALLOWED_TYPES_empty_not_allowed( # noqa: N802 + app_environment: EnvVarsDict, monkeypatch: pytest.MonkeyPatch +): + monkeypatch.setenv("EC2_INSTANCES_ALLOWED_TYPES", "{}") + + with pytest.raises(ValidationError): + ApplicationSettings.create_from_envs() def test_invalid_instance_names( From 2098da17ae9d23992940d25c2930ffbb2629dc1f Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:34:01 +0200 Subject: [PATCH 12/17] updates api --- .../src/aws_library/ec2/_client.py | 19 ++++++++++++++++--- packages/aws-library/tests/test_ec2_client.py | 11 +++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/aws-library/src/aws_library/ec2/_client.py b/packages/aws-library/src/aws_library/ec2/_client.py index 7ce24461b0c..29d0f841068 100644 --- a/packages/aws-library/src/aws_library/ec2/_client.py +++ b/packages/aws-library/src/aws_library/ec2/_client.py @@ -30,6 +30,9 @@ _logger = logging.getLogger(__name__) +ALL = None + + @dataclass() class SimcoreEC2API: client: EC2Client @@ -66,20 +69,30 @@ async def ping(self) -> bool: @ec2_exception_handler(_logger) async def get_ec2_instance_capabilities( self, - instance_type_names: set[InstanceTypeType], + instance_type_names: set[InstanceTypeType] | None = ALL, ) -> list[EC2InstanceType]: """Returns the ec2 instance types from a list of instance type names (sorted by name) Arguments: - instance_type_names -- the types to filter with. If an empty set, it returns all. + instance_type_names -- the types to filter with Raises: Ec2InstanceTypeInvalidError: some invalid types were used as filter ClustersKeeperRuntimeError: unexpected error communicating with EC2 """ + if instance_type_names is None: + assert ALL is None # nosec + selected_instance_types = [] + else: + selected_instance_types = list(instance_type_names) + + if len(selected_instance_types) == 0: + msg = "`instance_type_names` cannot be an empty set. Set as None if all" + raise ValueError(msg) + instance_types = await self.client.describe_instance_types( - InstanceTypes=list(instance_type_names) + InstanceTypes=selected_instance_types ) list_instances: list[EC2InstanceType] = [] for instance in instance_types.get("InstanceTypes", []): diff --git a/packages/aws-library/tests/test_ec2_client.py b/packages/aws-library/tests/test_ec2_client.py index 5eb8017898a..43d1b17bb2b 100644 --- a/packages/aws-library/tests/test_ec2_client.py +++ b/packages/aws-library/tests/test_ec2_client.py @@ -119,15 +119,22 @@ async def test_get_ec2_instance_capabilities( assert [_.name for _ in instance_types] == sorted(ec2_allowed_instances) -async def test_get_ec2_instance_capabilities_empty_list_returns_all_options( +async def test_get_ec2_instance_capabilities_returns_all_options( simcore_ec2_api: SimcoreEC2API, ): - instance_types = await simcore_ec2_api.get_ec2_instance_capabilities(set()) + instance_types = await simcore_ec2_api.get_ec2_instance_capabilities() assert instance_types # NOTE: this might need adaptation when moto is updated assert 700 < len(instance_types) < 828 +async def test_get_ec2_instance_capabilities_raise_with_empty_set( + simcore_ec2_api: SimcoreEC2API, +): + with pytest.raises(ValueError, match="instance_type_names"): + await simcore_ec2_api.get_ec2_instance_capabilities(set()) + + async def test_get_ec2_instance_capabilities_with_invalid_type_raises( simcore_ec2_api: SimcoreEC2API, faker: Faker, From d44b9729d6303e61662eadd89ea005b7af2c8b0b Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:36:32 +0200 Subject: [PATCH 13/17] rm tests --- .../modules/auto_scaling_core.py | 6 +-- .../unit/test_modules_auto_scaling_core.py | 51 ------------------- 2 files changed, 3 insertions(+), 54 deletions(-) delete mode 100644 services/autoscaling/tests/unit/test_modules_auto_scaling_core.py diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index a301ac0e896..991918b4073 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -333,14 +333,13 @@ async def _sorted_allowed_instance_types(app: FastAPI) -> list[EC2InstanceType]: assert app_settings.AUTOSCALING_EC2_INSTANCES # nosec ec2_client = get_ec2_client(app) - # some instances might be able to run several tasks allowed_instance_type_names = list( app_settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES ) - assert ( + assert ( # nosec allowed_instance_type_names - ), "EC2_INSTANCES_ALLOWED_TYPES cannot be empty!" # nosec + ), "EC2_INSTANCES_ALLOWED_TYPES cannot be empty!" allowed_instance_types: list[ EC2InstanceType @@ -352,6 +351,7 @@ def _as_selection(instance_type: EC2InstanceType) -> int: # NOTE: will raise ValueError if allowed_instance_types not in allowed_instance_type_names return allowed_instance_type_names.index(f"{instance_type.name}") + # some instances might be able to run several tasks allowed_instance_types.sort(key=_as_selection) return allowed_instance_types diff --git a/services/autoscaling/tests/unit/test_modules_auto_scaling_core.py b/services/autoscaling/tests/unit/test_modules_auto_scaling_core.py deleted file mode 100644 index aca17290ee0..00000000000 --- a/services/autoscaling/tests/unit/test_modules_auto_scaling_core.py +++ /dev/null @@ -1,51 +0,0 @@ -# pylint: disable=redefined-outer-name -# pylint: disable=unused-argument -# pylint: disable=unused-variable -# pylint: disable=too-many-arguments - - -import logging - -import pytest -from fastapi import FastAPI -from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict -from simcore_service_autoscaling.modules.auto_scaling_core import ( - _sorted_allowed_instance_types, -) - - -@pytest.fixture -def with_empty_ec2_intances_allowed_types( - monkeypatch: pytest.MonkeyPatch, - app_environment: EnvVarsDict, - disabled_rabbitmq: None, - mocked_ec2_server_envs: EnvVarsDict, - mocked_ssm_server_envs: EnvVarsDict, - mocked_redis_server: None, -) -> EnvVarsDict: - return app_environment | setenvs_from_dict( - monkeypatch, - { - "EC2_INSTANCES_ALLOWED_TYPES": "{}", - }, - ) - - -async def test_sorted_allowed_instance_types__warns_with_all_available( - with_empty_ec2_intances_allowed_types: EnvVarsDict, - initialized_app: FastAPI, - caplog: pytest.LogCaptureFixture, -): - app = initialized_app - - selected_names = list( - app.state.settings.AUTOSCALING_EC2_INSTANCES.EC2_INSTANCES_ALLOWED_TYPES - ) - assert not selected_names - - with caplog.at_level(logging.WARNING): - allowed = await _sorted_allowed_instance_types(app) - assert allowed - assert len(allowed) > 100 - - assert len(caplog.records) == 1 From 135bd65423482bcb031392bf7e4c47badc7348a6 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:48:30 +0200 Subject: [PATCH 14/17] fix --- packages/aws-library/src/aws_library/ec2/_client.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/aws-library/src/aws_library/ec2/_client.py b/packages/aws-library/src/aws_library/ec2/_client.py index 29d0f841068..02cc8045337 100644 --- a/packages/aws-library/src/aws_library/ec2/_client.py +++ b/packages/aws-library/src/aws_library/ec2/_client.py @@ -86,10 +86,9 @@ async def get_ec2_instance_capabilities( selected_instance_types = [] else: selected_instance_types = list(instance_type_names) - - if len(selected_instance_types) == 0: - msg = "`instance_type_names` cannot be an empty set. Set as None if all" - raise ValueError(msg) + if len(selected_instance_types) == 0: + msg = "`instance_type_names` cannot be an empty set. Set as None if all" + raise ValueError(msg) instance_types = await self.client.describe_instance_types( InstanceTypes=selected_instance_types From 63bdc45d5fa23a9f1ef9d8282dfde0e88bbee3aa Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:49:36 +0200 Subject: [PATCH 15/17] rename --- packages/aws-library/src/aws_library/ec2/_client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/aws-library/src/aws_library/ec2/_client.py b/packages/aws-library/src/aws_library/ec2/_client.py index 02cc8045337..9359e38d017 100644 --- a/packages/aws-library/src/aws_library/ec2/_client.py +++ b/packages/aws-library/src/aws_library/ec2/_client.py @@ -83,15 +83,15 @@ async def get_ec2_instance_capabilities( """ if instance_type_names is None: assert ALL is None # nosec - selected_instance_types = [] + selection_or_all_if_empty = [] else: - selected_instance_types = list(instance_type_names) - if len(selected_instance_types) == 0: + selection_or_all_if_empty = list(instance_type_names) + if len(selection_or_all_if_empty) == 0: msg = "`instance_type_names` cannot be an empty set. Set as None if all" raise ValueError(msg) instance_types = await self.client.describe_instance_types( - InstanceTypes=selected_instance_types + InstanceTypes=selection_or_all_if_empty ) list_instances: list[EC2InstanceType] = [] for instance in instance_types.get("InstanceTypes", []): From c899568cfa6054b306af1bead9c21987af4d9131 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:20:41 +0200 Subject: [PATCH 16/17] rm default and adds explicit --- .../aws-library/src/aws_library/ec2/_client.py | 14 +++++--------- packages/aws-library/tests/test_ec2_client.py | 2 +- .../clusters_keeper/ec2_instances.py | 4 +++- .../rpc/ec2_instances.py | 4 +++- .../tests/unit/test_rpc_ec2_instances.py | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/aws-library/src/aws_library/ec2/_client.py b/packages/aws-library/src/aws_library/ec2/_client.py index 9359e38d017..a40cf794304 100644 --- a/packages/aws-library/src/aws_library/ec2/_client.py +++ b/packages/aws-library/src/aws_library/ec2/_client.py @@ -2,7 +2,7 @@ import logging from collections.abc import Iterable, Sequence from dataclasses import dataclass -from typing import cast +from typing import Literal, cast import aioboto3 import botocore.exceptions @@ -30,9 +30,6 @@ _logger = logging.getLogger(__name__) -ALL = None - - @dataclass() class SimcoreEC2API: client: EC2Client @@ -69,25 +66,24 @@ async def ping(self) -> bool: @ec2_exception_handler(_logger) async def get_ec2_instance_capabilities( self, - instance_type_names: set[InstanceTypeType] | None = ALL, + instance_type_names: set[InstanceTypeType] | Literal["ALL"], ) -> list[EC2InstanceType]: """Returns the ec2 instance types from a list of instance type names (sorted by name) Arguments: - instance_type_names -- the types to filter with + instance_type_names -- the types to filter with or "ALL", to return all EC2 possible instances Raises: Ec2InstanceTypeInvalidError: some invalid types were used as filter ClustersKeeperRuntimeError: unexpected error communicating with EC2 """ - if instance_type_names is None: - assert ALL is None # nosec + if instance_type_names == "ALL": selection_or_all_if_empty = [] else: selection_or_all_if_empty = list(instance_type_names) if len(selection_or_all_if_empty) == 0: - msg = "`instance_type_names` cannot be an empty set. Set as None if all" + msg = "`instance_type_names` cannot be an empty set. Use either a selection or 'ALL'" raise ValueError(msg) instance_types = await self.client.describe_instance_types( diff --git a/packages/aws-library/tests/test_ec2_client.py b/packages/aws-library/tests/test_ec2_client.py index 43d1b17bb2b..b940383fdd3 100644 --- a/packages/aws-library/tests/test_ec2_client.py +++ b/packages/aws-library/tests/test_ec2_client.py @@ -122,7 +122,7 @@ async def test_get_ec2_instance_capabilities( async def test_get_ec2_instance_capabilities_returns_all_options( simcore_ec2_api: SimcoreEC2API, ): - instance_types = await simcore_ec2_api.get_ec2_instance_capabilities() + instance_types = await simcore_ec2_api.get_ec2_instance_capabilities("ALL") assert instance_types # NOTE: this might need adaptation when moto is updated assert 700 < len(instance_types) < 828 diff --git a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/clusters_keeper/ec2_instances.py b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/clusters_keeper/ec2_instances.py index 433eee07fa3..9358c60f7ca 100644 --- a/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/clusters_keeper/ec2_instances.py +++ b/packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/clusters_keeper/ec2_instances.py @@ -1,3 +1,5 @@ +from typing import Literal + from models_library.api_schemas_clusters_keeper import CLUSTERS_KEEPER_RPC_NAMESPACE from models_library.api_schemas_clusters_keeper.ec2_instances import EC2InstanceTypeGet from models_library.rabbitmq_basic_types import RPCMethodName @@ -7,7 +9,7 @@ async def get_instance_type_details( - client: RabbitMQRPCClient, *, instance_type_names: set[str] + client: RabbitMQRPCClient, *, instance_type_names: set[str] | Literal["ALL"] ) -> list[EC2InstanceTypeGet]: """**Remote method** diff --git a/services/clusters-keeper/src/simcore_service_clusters_keeper/rpc/ec2_instances.py b/services/clusters-keeper/src/simcore_service_clusters_keeper/rpc/ec2_instances.py index 6578f5169d6..0b1e6a4c5a5 100644 --- a/services/clusters-keeper/src/simcore_service_clusters_keeper/rpc/ec2_instances.py +++ b/services/clusters-keeper/src/simcore_service_clusters_keeper/rpc/ec2_instances.py @@ -1,3 +1,5 @@ +from typing import Literal + from aws_library.ec2 import EC2InstanceType from fastapi import FastAPI from models_library.api_schemas_clusters_keeper.ec2_instances import EC2InstanceTypeGet @@ -10,7 +12,7 @@ @router.expose() async def get_instance_type_details( - app: FastAPI, *, instance_type_names: set[str] + app: FastAPI, *, instance_type_names: set[str] | Literal["ALL"] ) -> list[EC2InstanceTypeGet]: instance_capabilities: list[EC2InstanceType] = await get_ec2_client( app diff --git a/services/clusters-keeper/tests/unit/test_rpc_ec2_instances.py b/services/clusters-keeper/tests/unit/test_rpc_ec2_instances.py index f4eea132cdf..a833d13743e 100644 --- a/services/clusters-keeper/tests/unit/test_rpc_ec2_instances.py +++ b/services/clusters-keeper/tests/unit/test_rpc_ec2_instances.py @@ -37,7 +37,7 @@ async def test_get_instance_type_details_all_options( # an empty set returns all options rpc_response = await get_instance_type_details( - clusters_keeper_rabbitmq_rpc_client, instance_type_names=set() + clusters_keeper_rabbitmq_rpc_client, instance_type_names="ALL" ) assert rpc_response assert isinstance(rpc_response, list) From f9c5e87e7aa507a6ebf6247673483f84af8358bd Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:30:52 +0200 Subject: [PATCH 17/17] @sanderegg review: doc --- .../simcore_service_autoscaling/modules/auto_scaling_core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py index 991918b4073..e7fc947cba5 100644 --- a/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py +++ b/services/autoscaling/src/simcore_service_autoscaling/modules/auto_scaling_core.py @@ -351,7 +351,6 @@ def _as_selection(instance_type: EC2InstanceType) -> int: # NOTE: will raise ValueError if allowed_instance_types not in allowed_instance_type_names return allowed_instance_type_names.index(f"{instance_type.name}") - # some instances might be able to run several tasks allowed_instance_types.sort(key=_as_selection) return allowed_instance_types @@ -501,7 +500,7 @@ async def _assign_tasks_to_current_cluster( ) -> tuple[list, Cluster]: """ Evaluates whether a task can be executed on any instance within the cluster. If the task's resource requirements are met, the task is *denoted* as assigned to the cluster. - Note: This is an estimation only since actual scheduling is handled by Dask. + Note: This is an estimation only since actual scheduling is handled by Dask/Docker (depending on the mode). Returns: A tuple containing: