Skip to content

Commit 1b728af

Browse files
GitHKAndrei Neagu
authored andcommitted
♻️ Polished and modernised agent service ⚠️ (ITISFoundation#6452)
Co-authored-by: Andrei Neagu <[email protected]>
1 parent 2dfed79 commit 1b728af

File tree

79 files changed

+1846
-2207
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1846
-2207
lines changed

packages/models-library/src/models_library/api_schemas_directorv2/services.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, ClassVar
1+
from typing import Any, ClassVar, Final
22

33
from pydantic import BaseModel, Field, validator
44
from pydantic.types import ByteSize, NonNegativeInt
@@ -90,3 +90,6 @@ class Config:
9090
for node_example in NodeRequirements.Config.schema_extra["examples"]
9191
]
9292
}
93+
94+
95+
CHARS_IN_VOLUME_NAME_BEFORE_DIR_NAME: Final[NonNegativeInt] = 89

packages/service-library/src/servicelib/fastapi/app_state.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import logging
1+
from typing import TypeVar
22

33
from fastapi import FastAPI
44

5-
_logger = logging.getLogger(__name__)
5+
T = TypeVar("T", bound="SingletonInAppStateMixin")
66

77

88
class SingletonInAppStateMixin:
@@ -14,8 +14,8 @@ class SingletonInAppStateMixin:
1414
frozen: bool = True # Will raise if set multiple times
1515

1616
@classmethod
17-
def get_from_app_state(cls, app: FastAPI):
18-
return getattr(app.state, cls.app_state_name)
17+
def get_from_app_state(cls: type[T], app: FastAPI) -> T:
18+
return getattr(app.state, cls.app_state_name) # type:ignore[no-any-return]
1919

2020
def set_to_app_state(self, app: FastAPI):
2121
if (exists := getattr(app.state, self.app_state_name, None)) and self.frozen:
@@ -26,11 +26,11 @@ def set_to_app_state(self, app: FastAPI):
2626
return self.get_from_app_state(app)
2727

2828
@classmethod
29-
def pop_from_app_state(cls, app: FastAPI):
29+
def pop_from_app_state(cls: type[T], app: FastAPI) -> T:
3030
"""
3131
Raises:
3232
AttributeError: if instance is not in app.state
3333
"""
34-
old = getattr(app.state, cls.app_state_name)
34+
old = cls.get_from_app_state(app)
3535
delattr(app.state, cls.app_state_name)
3636
return old
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from pydantic.errors import PydanticErrorMixin
2+
3+
4+
class BaseAgentRPCError(PydanticErrorMixin, Exception):
5+
...
6+
7+
8+
class NoServiceVolumesFoundRPCError(BaseAgentRPCError):
9+
msg_template: str = (
10+
"Could not detect any unused volumes after waiting '{period}' seconds for "
11+
"volumes to be released after closing all container for service='{node_id}'"
12+
)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import logging
2+
from datetime import timedelta
3+
from typing import Final
4+
5+
from models_library.projects_nodes_io import NodeID
6+
from models_library.rabbitmq_basic_types import RPCMethodName, RPCNamespace
7+
from pydantic import NonNegativeInt, parse_obj_as
8+
from servicelib.logging_utils import log_decorator
9+
from servicelib.rabbitmq import RabbitMQRPCClient
10+
11+
_logger = logging.getLogger(__name__)
12+
13+
_REQUEST_TIMEOUT: Final[NonNegativeInt] = int(timedelta(minutes=60).total_seconds())
14+
15+
16+
@log_decorator(_logger, level=logging.DEBUG)
17+
async def remove_volumes_without_backup_for_service(
18+
rabbitmq_rpc_client: RabbitMQRPCClient,
19+
*,
20+
docker_node_id: str,
21+
swarm_stack_name: str,
22+
node_id: NodeID,
23+
) -> None:
24+
result = await rabbitmq_rpc_client.request(
25+
RPCNamespace.from_entries(
26+
{
27+
"service": "agent",
28+
"docker_node_id": docker_node_id,
29+
"swarm_stack_name": swarm_stack_name,
30+
}
31+
),
32+
parse_obj_as(RPCMethodName, "remove_volumes_without_backup_for_service"),
33+
node_id=node_id,
34+
timeout_s=_REQUEST_TIMEOUT,
35+
)
36+
assert result is None # nosec
37+
38+
39+
@log_decorator(_logger, level=logging.DEBUG)
40+
async def backup_and_remove_volumes_for_all_services(
41+
rabbitmq_rpc_client: RabbitMQRPCClient,
42+
*,
43+
docker_node_id: str,
44+
swarm_stack_name: str,
45+
) -> None:
46+
result = await rabbitmq_rpc_client.request(
47+
RPCNamespace.from_entries(
48+
{
49+
"service": "agent",
50+
"docker_node_id": docker_node_id,
51+
"swarm_stack_name": swarm_stack_name,
52+
}
53+
),
54+
parse_obj_as(RPCMethodName, "backup_and_remove_volumes_for_all_services"),
55+
timeout_s=_REQUEST_TIMEOUT,
56+
)
57+
assert result is None # nosec

services/agent/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.1
1+
1.0.0

services/agent/requirements/_base.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
# intra-repo required dependencies
99
--requirement ../../../packages/models-library/requirements/_base.in
1010
--requirement ../../../packages/settings-library/requirements/_base.in
11+
# service-library[fastapi]
12+
--requirement ../../../packages/service-library/requirements/_base.in
1113
--requirement ../../../packages/service-library/requirements/_fastapi.in
1214

1315
aiodocker
1416
fastapi
1517
packaging
1618
pydantic
17-
python-dotenv
1819
uvicorn

0 commit comments

Comments
 (0)