Skip to content

Commit d386338

Browse files
adding tests
1 parent 6a75a26 commit d386338

File tree

11 files changed

+234
-38
lines changed

11 files changed

+234
-38
lines changed

packages/models-library/src/models_library/rabbitmq_messages.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class DynamicServiceRunningMessage(RabbitMessageBase):
201201

202202
project_id: ProjectID
203203
node_id: NodeID
204+
user_id: UserID
204205
product_name: ProductName | None
205206
created_at: datetime.datetime = Field(
206207
default_factory=lambda: arrow.utcnow().datetime,

services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/resource_tracking/_core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ async def _heart_beat_task(app: FastAPI):
8181
dyn_message = DynamicServiceRunningMessage(
8282
project_id=settings.DY_SIDECAR_PROJECT_ID,
8383
node_id=settings.DY_SIDECAR_NODE_ID,
84+
user_id=settings.DY_SIDECAR_USER_ID,
8485
product_name=settings.DY_SIDECAR_PRODUCT_NAME,
8586
)
8687
await asyncio.gather(

services/efs-guardian/src/simcore_service_efs_guardian/core/settings.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
LogLevel,
99
VersionTag,
1010
)
11-
from pydantic import Field, PositiveInt, validator
11+
from pydantic import ByteSize, Field, PositiveInt, validator
1212
from settings_library.base import BaseCustomSettings
1313
from settings_library.efs import AwsEfsSettings
1414
from settings_library.rabbit import RabbitSettings
@@ -58,6 +58,9 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
5858
EFS_GROUP_NAME: str = Field(
5959
description="Linux group name that the EFS and Simcore linux users are part of"
6060
)
61+
EFS_DEFAULT_USER_SERVICE_SIZE_BYTES: ByteSize = Field(
62+
default=536870912000 # 500GiB = 534GB
63+
)
6164

6265
# RUNTIME -----------------------------------------------------------
6366
EFS_GUARDIAN_DEBUG: bool = Field(

services/efs-guardian/src/simcore_service_efs_guardian/services/background_tasks_setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from servicelib.redis_utils import start_exclusive_periodic_task
1111

1212
from .background_tasks import removal_policy_task
13-
from .modules.redis import get_redis_client
13+
from .modules.redis import get_redis_lock_client
1414

1515
_logger = logging.getLogger(__name__)
1616

@@ -37,7 +37,7 @@ async def _startup() -> None:
3737
# Setup periodic tasks
3838
for task in _EFS_GUARDIAN_BACKGROUND_TASKS:
3939
exclusive_task = start_exclusive_periodic_task(
40-
get_redis_client(app),
40+
get_redis_lock_client(app),
4141
task["task_func"],
4242
task_period=timedelta(seconds=60), # 1 minute
4343
retry_after=timedelta(seconds=60), # 5 minutes

services/efs-guardian/src/simcore_service_efs_guardian/services/efs_manager_utils.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def get_size_bash_async(path) -> ByteSize:
2828
raise e
2929

3030

31-
async def remove_write_permissions_bash_async(path) -> ByteSize:
31+
async def remove_write_permissions_bash_async(path) -> None:
3232
try:
3333
# Create the subprocess
3434
process = await asyncio.create_subprocess_exec(
@@ -44,9 +44,7 @@ async def remove_write_permissions_bash_async(path) -> ByteSize:
4444
stdout, stderr = await process.communicate()
4545

4646
if process.returncode == 0:
47-
# Parse the output
48-
size = ByteSize(stdout.decode().split()[0])
49-
return size
47+
return
5048
else:
5149
print(f"Error: {stderr.decode()}")
5250
raise ValueError

services/efs-guardian/src/simcore_service_efs_guardian/services/modules/redis.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@
1010

1111
def setup(app: FastAPI) -> None:
1212
async def on_startup() -> None:
13-
app.state.redis_client_sdk = None
13+
app.state.redis_lock_client_sdk = None
1414
settings: RedisSettings = app.state.settings.EFS_GUARDIAN_REDIS
1515
redis_locks_dsn = settings.build_redis_dsn(RedisDatabase.LOCKS)
16-
app.state.redis_client_sdk = client = RedisClientSDK(redis_locks_dsn)
17-
await client.setup()
16+
app.state.redis_lock_client_sdk = lock_client = RedisClientSDK(redis_locks_dsn)
17+
await lock_client.setup()
1818

1919
async def on_shutdown() -> None:
20-
redis_client_sdk: None | RedisClientSDK = app.state.redis_client_sdk
21-
if redis_client_sdk:
22-
await redis_client_sdk.shutdown()
20+
redis_lock_client_sdk: None | RedisClientSDK = app.state.redis_lock_client_sdk
21+
if redis_lock_client_sdk:
22+
await redis_lock_client_sdk.shutdown()
2323

2424
app.add_event_handler("startup", on_startup)
2525
app.add_event_handler("shutdown", on_shutdown)
2626

2727

28-
def get_redis_client(app: FastAPI) -> RedisClientSDK:
29-
return cast(RedisClientSDK, app.state.redis_client_sdk)
28+
def get_redis_lock_client(app: FastAPI) -> RedisClientSDK:
29+
return cast(RedisClientSDK, app.state.redis_lock_client_sdk)

services/efs-guardian/src/simcore_service_efs_guardian/services/process_messages.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from fastapi import FastAPI
44
from models_library.rabbitmq_messages import DynamicServiceRunningMessage
55
from pydantic import parse_raw_as
6+
from simcore_service_efs_guardian.services.modules.redis import get_redis_lock_client
7+
8+
from ..core.settings import get_application_settings
9+
from ..services.efs_manager import EfsManager
610

711
_logger = logging.getLogger(__name__)
812

@@ -12,9 +16,33 @@ async def process_dynamic_service_running_message(app: FastAPI, data: bytes) ->
1216
rabbit_message: DynamicServiceRunningMessage = parse_raw_as(
1317
DynamicServiceRunningMessage, data # type: ignore[arg-type]
1418
)
15-
_logger.info(
16-
"Process %s msg service_run_id: %s",
19+
_logger.debug(
20+
"Process dynamic service running msg, project ID: %s node ID: %s",
1721
rabbit_message.project_id,
1822
rabbit_message.node_id,
1923
)
24+
25+
settings = get_application_settings(app)
26+
efs_manager: EfsManager = app.state.efs_manager
27+
size = await efs_manager.get_project_node_data_size(
28+
rabbit_message.project_id, node_id=rabbit_message.node_id
29+
)
30+
31+
if size > settings.EFS_DEFAULT_USER_SERVICE_SIZE_BYTES:
32+
_logger.warning(
33+
"Removing write permissions inside of EFS starts for project ID: %s, node ID: %s, current user: %s",
34+
rabbit_message.project_id,
35+
rabbit_message.node_id,
36+
rabbit_message.user_id,
37+
)
38+
redis = get_redis_lock_client(app)
39+
async with redis.lock_context(
40+
f"efs_remove_write_permissions-{rabbit_message.project_id=}-{rabbit_message.node_id=}",
41+
blocking=True,
42+
blocking_timeout_s=10,
43+
):
44+
await efs_manager.remove_project_node_data_write_permissions(
45+
project_id=rabbit_message.project_id, node_id=rabbit_message.node_id
46+
)
47+
2048
return True

services/efs-guardian/tests/unit/conftest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
import simcore_service_efs_guardian
1313
import yaml
1414
from asgi_lifespan import LifespanManager
15+
from fakeredis.aioredis import FakeRedis
1516
from fastapi import FastAPI
1617
from httpx import ASGITransport
18+
from pytest_mock import MockerFixture
1719
from pytest_simcore.helpers.monkeypatch_envs import EnvVarsDict, setenvs_from_dict
1820
from servicelib.rabbitmq import RabbitMQRPCClient
1921
from settings_library.rabbit import RabbitSettings
@@ -29,6 +31,7 @@
2931
"pytest_simcore.pydantic_models",
3032
"pytest_simcore.pytest_global_environs",
3133
"pytest_simcore.rabbit_service",
34+
"pytest_simcore.redis_service",
3235
"pytest_simcore.repository_paths",
3336
"pytest_simcore.aws_s3_service",
3437
"pytest_simcore.aws_server",
@@ -139,3 +142,9 @@ async def rpc_client(
139142
rabbitmq_rpc_client: Callable[[str], Awaitable[RabbitMQRPCClient]],
140143
) -> RabbitMQRPCClient:
141144
return await rabbitmq_rpc_client("client")
145+
146+
147+
@pytest.fixture
148+
async def mocked_redis_server(mocker: MockerFixture) -> None:
149+
mock_redis = FakeRedis()
150+
mocker.patch("redis.asyncio.from_url", return_value=mock_redis)

services/efs-guardian/tests/unit/test_api_health.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ def app_environment(
2828
)
2929

3030

31-
async def test_healthcheck(rabbit_service: RabbitSettings, client: httpx.AsyncClient):
31+
async def test_healthcheck(
32+
rabbit_service: RabbitSettings,
33+
mocked_redis_server,
34+
client: httpx.AsyncClient,
35+
):
3236
response = await client.get("/")
3337
response.raise_for_status()
3438
assert response.status_code == status.HTTP_200_OK
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# pylint: disable=protected-access
2+
# pylint: disable=redefined-outer-name
3+
# pylint: disable=too-many-arguments
4+
# pylint: disable=unused-argument
5+
# pylint: disable=unused-variable
6+
7+
from pathlib import Path
8+
from unittest.mock import patch
9+
10+
import pytest
11+
from faker import Faker
12+
from fastapi import FastAPI
13+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
14+
from pytest_simcore.helpers.typing_env import EnvVarsDict
15+
from servicelib.rabbitmq import RabbitMQRPCClient
16+
from servicelib.rabbitmq.rpc_interfaces.efs_guardian import efs_manager
17+
from simcore_service_efs_guardian.core.settings import AwsEfsSettings
18+
19+
pytest_simcore_core_services_selection = ["rabbit"]
20+
pytest_simcore_ops_services_selection = []
21+
22+
23+
@pytest.fixture
24+
def app_environment(
25+
monkeypatch: pytest.MonkeyPatch,
26+
app_environment: EnvVarsDict,
27+
rabbit_env_vars_dict: EnvVarsDict, # rabbitMQ settings from 'rabbit' service
28+
) -> EnvVarsDict:
29+
return setenvs_from_dict(
30+
monkeypatch,
31+
{
32+
**app_environment,
33+
**rabbit_env_vars_dict,
34+
},
35+
)
36+
37+
38+
async def test_rpc_create_project_specific_data_dir(
39+
rpc_client: RabbitMQRPCClient,
40+
faker: Faker,
41+
mocked_redis_server: None,
42+
app: FastAPI,
43+
):
44+
aws_efs_settings: AwsEfsSettings = app.state.settings.EFS_GUARDIAN_AWS_EFS_SETTINGS
45+
46+
_project_id = faker.uuid4()
47+
_node_id = faker.uuid4()
48+
_storage_directory_name = faker.name()
49+
50+
with patch(
51+
"simcore_service_efs_guardian.services.efs_manager.os.chown"
52+
) as mocked_chown:
53+
result = await efs_manager.create_project_specific_data_dir(
54+
rpc_client,
55+
project_id=_project_id,
56+
node_id=_node_id,
57+
storage_directory_name=_storage_directory_name,
58+
)
59+
mocked_chown.assert_called_once()
60+
61+
assert isinstance(result, Path)
62+
_expected_path = (
63+
aws_efs_settings.EFS_MOUNTED_PATH
64+
/ aws_efs_settings.EFS_PROJECT_SPECIFIC_DATA_DIRECTORY
65+
/ _project_id
66+
/ _node_id
67+
/ _storage_directory_name
68+
)
69+
assert _expected_path == result
70+
assert _expected_path.exists

0 commit comments

Comments
 (0)