-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added rabbitmq utils, refactor and tests
- Loading branch information
Showing
5 changed files
with
190 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 40 additions & 13 deletions
53
services/autoscaling/src/simcore_service_autoscaling/utils/rabbitmq.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,50 @@ | ||
import logging | ||
|
||
from fastapi import FastAPI | ||
from models_library.generated_models.docker_rest_api import Node, Task | ||
from models_library.rabbitmq_messages import AutoscalingStatus, RabbitAutoscalingMessage | ||
from models_library.rabbitmq_messages import ( | ||
AutoscalingStatus, | ||
LoggerRabbitMessage, | ||
RabbitAutoscalingMessage, | ||
) | ||
from servicelib.logging_utils import log_catch | ||
|
||
from ..models import Resources, SimcoreServiceDockerLabelKeys | ||
from ..rabbitmq import post_message | ||
|
||
from ..models import Resources | ||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def create_rabbit_message( | ||
async def post_state_message( | ||
app: FastAPI, | ||
monitored_nodes: list[Node], | ||
cluster_total_resources: Resources, | ||
cluster_used_resources: Resources, | ||
pending_tasks: list[Task], | ||
status: AutoscalingStatus, | ||
) -> RabbitAutoscalingMessage: | ||
return RabbitAutoscalingMessage( | ||
origin=app.title, | ||
number_monitored_nodes=len(monitored_nodes), | ||
cluster_total_resources=cluster_total_resources.dict(), | ||
cluster_used_resources=cluster_used_resources.dict(), | ||
number_pending_tasks_without_resources=len(pending_tasks), | ||
status=status, | ||
) | ||
) -> None: | ||
with log_catch(logger, reraise=False): | ||
message = RabbitAutoscalingMessage( | ||
origin=app.title, | ||
number_monitored_nodes=len(monitored_nodes), | ||
cluster_total_resources=cluster_total_resources.dict(), | ||
cluster_used_resources=cluster_used_resources.dict(), | ||
number_pending_tasks_without_resources=len(pending_tasks), | ||
status=AutoscalingStatus.SCALING_UP | ||
if pending_tasks | ||
else AutoscalingStatus.IDLE, | ||
) | ||
logger.debug("autoscaling state: %s", message) | ||
await post_message(app, message) | ||
|
||
|
||
async def post_log_message(app: FastAPI, task: Task, log: str, level: int): | ||
with log_catch(logger, reraise=False): | ||
simcore_label_keys = SimcoreServiceDockerLabelKeys.from_docker_task(task) | ||
message = LoggerRabbitMessage( | ||
node_id=simcore_label_keys.node_id, | ||
user_id=simcore_label_keys.user_id, | ||
project_id=simcore_label_keys.project_id, | ||
messages=[log], | ||
) | ||
logger.log(level, message) | ||
await post_message(app, message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# pylint:disable=unused-variable | ||
# pylint:disable=unused-argument | ||
# pylint:disable=redefined-outer-name | ||
|
||
|
||
from typing import Any, Awaitable, Callable, Mapping | ||
|
||
import aiodocker | ||
from faker import Faker | ||
from fastapi import FastAPI | ||
from models_library.generated_models.docker_rest_api import Task | ||
from models_library.rabbitmq_messages import LoggerRabbitMessage | ||
from pydantic import parse_obj_as | ||
from pytest_mock.plugin import MockerFixture | ||
from servicelib.rabbitmq import RabbitMQClient | ||
from settings_library.rabbit import RabbitSettings | ||
from simcore_service_autoscaling.models import SimcoreServiceDockerLabelKeys | ||
from simcore_service_autoscaling.utils.rabbitmq import post_log_message | ||
from tenacity._asyncio import AsyncRetrying | ||
from tenacity.retry import retry_if_exception_type | ||
from tenacity.stop import stop_after_delay | ||
from tenacity.wait import wait_fixed | ||
|
||
_TENACITY_RETRY_PARAMS = dict( | ||
reraise=True, | ||
retry=retry_if_exception_type(AssertionError), | ||
stop=stop_after_delay(30), | ||
wait=wait_fixed(0.5), | ||
) | ||
|
||
|
||
# Selection of core and tool services started in this swarm fixture (integration) | ||
pytest_simcore_core_services_selection = [ | ||
"rabbit", | ||
] | ||
|
||
pytest_simcore_ops_services_selection = [] | ||
|
||
|
||
async def test_post_log_message( | ||
disable_dynamic_service_background_task, | ||
enabled_rabbitmq: RabbitSettings, | ||
initialized_app: FastAPI, | ||
rabbit_client: RabbitMQClient, | ||
mocker: MockerFixture, | ||
async_docker_client: aiodocker.Docker, | ||
create_service: Callable[ | ||
[dict[str, Any], dict[str, str]], Awaitable[Mapping[str, Any]] | ||
], | ||
task_template: dict[str, Any], | ||
osparc_docker_label_keys: SimcoreServiceDockerLabelKeys, | ||
faker: Faker, | ||
): | ||
mocked_message_handler = mocker.AsyncMock(return_value=True) | ||
await rabbit_client.subscribe( | ||
LoggerRabbitMessage.get_channel_name(), mocked_message_handler | ||
) | ||
|
||
service_with_labels = await create_service( | ||
task_template, | ||
osparc_docker_label_keys.to_docker_labels(), | ||
) | ||
service_tasks = parse_obj_as( | ||
list[Task], | ||
await async_docker_client.tasks.list( | ||
filters={"service": service_with_labels["Spec"]["Name"]} | ||
), | ||
) | ||
assert service_tasks | ||
assert len(service_tasks) == 1 | ||
|
||
log_message = faker.pystr() | ||
await post_log_message(initialized_app, service_tasks[0], log_message, 0) | ||
|
||
async for attempt in AsyncRetrying(**_TENACITY_RETRY_PARAMS): | ||
with attempt: | ||
print( | ||
f"--> checking for message in rabbit exchange {LoggerRabbitMessage.get_channel_name()}, {attempt.retry_state.retry_object.statistics}" | ||
) | ||
mocked_message_handler.assert_called_once_with( | ||
LoggerRabbitMessage( | ||
node_id=osparc_docker_label_keys.node_id, | ||
project_id=osparc_docker_label_keys.project_id, | ||
user_id=osparc_docker_label_keys.user_id, | ||
messages=[log_message], | ||
) | ||
.json() | ||
.encode() | ||
) | ||
print("... message received") | ||
|
||
|
||
async def test_post_log_message_does_not_raise_if_service_has_no_labels( | ||
disable_dynamic_service_background_task, | ||
enabled_rabbitmq: RabbitSettings, | ||
initialized_app: FastAPI, | ||
async_docker_client: aiodocker.Docker, | ||
create_service: Callable[[dict[str, Any]], Awaitable[Mapping[str, Any]]], | ||
task_template: dict[str, Any], | ||
faker: Faker, | ||
): | ||
service_without_labels = await create_service(task_template) | ||
service_tasks = parse_obj_as( | ||
list[Task], | ||
await async_docker_client.tasks.list( | ||
filters={"service": service_without_labels["Spec"]["Name"]} | ||
), | ||
) | ||
assert service_tasks | ||
assert len(service_tasks) == 1 | ||
|
||
# this shall not raise any exception even if the task does not contain | ||
# the necessary labels | ||
await post_log_message(initialized_app, service_tasks[0], faker.pystr(), 0) |