Skip to content

Commit 295f20a

Browse files
authored
♻️Legacy archives are always deleted as the owner of the project (#4816)
1 parent e4cef38 commit 295f20a

File tree

4 files changed

+83
-54
lines changed

4 files changed

+83
-54
lines changed

packages/simcore-sdk/src/simcore_sdk/node_data/data_manager.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from ..node_ports_common import filemanager
1515
from ..node_ports_common.constants import SIMCORE_LOCATION
16+
from ..node_ports_common.dbmanager import DBManager
1617
from ..node_ports_common.file_io_utils import LogRedirectCB
1718

1819
_logger = logging.getLogger(__name__)
@@ -155,15 +156,20 @@ async def _state_metadata_entry_exists(
155156

156157

157158
async def _delete_legacy_archive(
158-
user_id: UserID, project_id: ProjectID, node_uuid: NodeID, path: Path
159+
project_id: ProjectID, node_uuid: NodeID, path: Path
159160
) -> None:
160161
"""removes the .zip state archive from storage"""
161162
s3_object = __create_s3_object_key(
162163
project_id, node_uuid, __get_s3_name(path, is_archive=True)
163164
)
164165
_logger.debug("Deleting s3_object='%s' is archive", s3_object)
166+
167+
# NOTE: if service is opened by a person which the users shared it with,
168+
# they will not have the permission to delete the node
169+
# Removing it via it's owner allows to always have access to the delete operation.
170+
owner_id = await DBManager().get_project_owner_user_id(project_id)
165171
await filemanager.delete_file(
166-
user_id=user_id, store_id=SIMCORE_LOCATION, s3_object=s3_object
172+
user_id=owner_id, store_id=SIMCORE_LOCATION, s3_object=s3_object
167173
)
168174

169175

@@ -203,7 +209,6 @@ async def push(
203209

204210
with log_context(_logger, logging.INFO, "removing legacy data archive"):
205211
await _delete_legacy_archive(
206-
user_id=user_id,
207212
project_id=project_id,
208213
node_uuid=node_uuid,
209214
path=source_path,

packages/simcore-sdk/src/simcore_sdk/node_ports_common/dbmanager.py

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,26 @@
22
import logging
33
import os
44
import socket
5+
from typing import Any
56

67
import aiopg.sa
78
import sqlalchemy as sa
89
import tenacity
910
from aiopg.sa.engine import Engine
1011
from aiopg.sa.result import RowProxy
12+
from models_library.projects import ProjectID
13+
from models_library.users import UserID
1114
from servicelib.common_aiopg_utils import DataSourceName, create_pg_engine
1215
from servicelib.retry_policies import PostgresRetryPolicyUponInitialization
1316
from simcore_postgres_database.models.comp_tasks import comp_tasks
17+
from simcore_postgres_database.models.projects import projects
1418
from simcore_postgres_database.utils_aiopg import (
1519
close_engine,
1620
raise_if_migration_not_ready,
1721
)
1822
from sqlalchemy import and_
1923

20-
from .exceptions import NodeNotFound
24+
from .exceptions import NodeNotFound, ProjectNotFoundError
2125
from .settings import NodePortsSettings
2226

2327
log = logging.getLogger(__name__)
@@ -110,45 +114,56 @@ async def write_ports_configuration(
110114
log.debug(message)
111115

112116
node_configuration = json.loads(json_configuration)
113-
async with DBContextManager(self._db_engine) as engine:
114-
async with engine.acquire() as connection:
115-
# update the necessary parts
116-
await connection.execute(
117-
# FIXME: E1120:No value for argument 'dml' in method call
118-
# pylint: disable=E1120
119-
comp_tasks.update()
120-
.where(
121-
and_(
122-
comp_tasks.c.node_id == node_uuid,
123-
comp_tasks.c.project_id == project_id,
124-
)
125-
)
126-
.values(
127-
schema=node_configuration["schema"],
128-
inputs=node_configuration["inputs"],
129-
outputs=node_configuration["outputs"],
130-
run_hash=node_configuration.get("run_hash"),
117+
async with DBContextManager(
118+
self._db_engine
119+
) as engine, engine.acquire() as connection:
120+
# update the necessary parts
121+
await connection.execute(
122+
comp_tasks.update()
123+
.where(
124+
and_(
125+
comp_tasks.c.node_id == node_uuid,
126+
comp_tasks.c.project_id == project_id,
131127
)
132128
)
129+
.values(
130+
schema=node_configuration["schema"],
131+
inputs=node_configuration["inputs"],
132+
outputs=node_configuration["outputs"],
133+
run_hash=node_configuration.get("run_hash"),
134+
)
135+
)
133136

134137
async def get_ports_configuration_from_node_uuid(
135138
self, project_id: str, node_uuid: str
136139
) -> str:
137140
log.debug(
138141
"Getting ports configuration of node %s from comp_tasks table", node_uuid
139142
)
140-
async with DBContextManager(self._db_engine) as engine:
141-
async with engine.acquire() as connection:
142-
node: RowProxy = await _get_node_from_db(
143-
project_id, node_uuid, connection
144-
)
145-
node_json_config = json.dumps(
146-
{
147-
"schema": node.schema,
148-
"inputs": node.inputs,
149-
"outputs": node.outputs,
150-
"run_hash": node.run_hash,
151-
}
152-
)
143+
async with DBContextManager(
144+
self._db_engine
145+
) as engine, engine.acquire() as connection:
146+
node: RowProxy = await _get_node_from_db(project_id, node_uuid, connection)
147+
node_json_config = json.dumps(
148+
{
149+
"schema": node.schema,
150+
"inputs": node.inputs,
151+
"outputs": node.outputs,
152+
"run_hash": node.run_hash,
153+
}
154+
)
153155
log.debug("Found and converted to json")
154156
return node_json_config
157+
158+
async def get_project_owner_user_id(self, project_id: ProjectID) -> UserID:
159+
async with DBContextManager(
160+
self._db_engine
161+
) as engine, engine.acquire() as connection:
162+
prj_owner: Any | None = await connection.scalar(
163+
sa.select(projects.c.prj_owner).where(
164+
projects.c.uuid == f"{project_id}"
165+
)
166+
)
167+
if prj_owner is None:
168+
raise ProjectNotFoundError(project_id)
169+
return UserID(prj_owner)

packages/simcore-sdk/src/simcore_sdk/node_ports_common/exceptions.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55
#
66
#
77

8-
from typing import Optional
9-
108

119
class NodeportsException(Exception):
1210
"""Basic exception for errors raised in nodeports"""
1311

14-
def __init__(self, msg: Optional[str] = None):
12+
def __init__(self, msg: str | None = None):
1513
super().__init__(msg or "An error occured in simcore")
1614

1715

@@ -26,23 +24,23 @@ def __init__(self, obj):
2624
class UnboundPortError(NodeportsException, IndexError):
2725
"""Accessed port is not configured"""
2826

29-
def __init__(self, port_index, msg: Optional[str] = None):
27+
def __init__(self, port_index, msg: str | None = None):
3028
super().__init__(f"No port bound at index {port_index}")
3129
self.port_index = port_index
3230

3331

3432
class InvalidKeyError(NodeportsException):
3533
"""Accessed key does not exist"""
3634

37-
def __init__(self, item_key: str, msg: Optional[str] = None):
35+
def __init__(self, item_key: str, msg: str | None = None):
3836
super().__init__(f"No port bound with key {item_key}")
3937
self.item_key = item_key
4038

4139

4240
class InvalidItemTypeError(NodeportsException):
4341
"""Item type incorrect"""
4442

45-
def __init__(self, item_type: str, item_value: str, msg: Optional[str] = None):
43+
def __init__(self, item_type: str, item_value: str, msg: str | None = None):
4644
super().__init__(
4745
msg
4846
or f"Invalid item type, value [{item_value}] does not qualify as type [{item_type}]"
@@ -54,7 +52,7 @@ def __init__(self, item_type: str, item_value: str, msg: Optional[str] = None):
5452
class InvalidProtocolError(NodeportsException):
5553
"""Invalid protocol used"""
5654

57-
def __init__(self, dct, msg: Optional[str] = None):
55+
def __init__(self, dct, msg: str | None = None):
5856
super().__init__(f"Invalid protocol used: {dct} [{msg}]")
5957
self.dct = dct
6058

@@ -70,7 +68,7 @@ class StorageServerIssue(NodeportsException):
7068
class S3TransferError(NodeportsException):
7169
"""S3 transfer error"""
7270

73-
def __init__(self, msg: Optional[str] = None):
71+
def __init__(self, msg: str | None = None):
7472
super().__init__(msg or "Error while transferring to/from S3 storage")
7573

7674

@@ -126,6 +124,14 @@ def __init__(self, node_uuid):
126124
super().__init__(f"the node id {node_uuid} was not found")
127125

128126

127+
class ProjectNotFoundError(NodeportsException):
128+
"""The given node_uuid was not found"""
129+
130+
def __init__(self, project_id):
131+
self.project_id = project_id
132+
super().__init__(f"the {project_id=} was not found")
133+
134+
129135
class SymlinkToSymlinkIsNotUploadableException(NodeportsException):
130136
"""Not possible to upload a symlink to a symlink"""
131137

packages/simcore-sdk/tests/integration/test_node_data_data_manager_.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _empty_path(path: Path) -> None:
5151
def _get_file_hashes_in_path(path_to_hash: Path) -> set[tuple[Path, str]]:
5252
def _hash_path(path: Path):
5353
sha256_hash = hashlib.sha256()
54-
with open(path, "rb") as f:
54+
with Path.open(path, "rb") as f:
5555
# Read and update hash string value in blocks of 4K
5656
for byte_block in iter(lambda: f.read(4096), b""):
5757
sha256_hash.update(byte_block)
@@ -156,7 +156,7 @@ async def test_valid_upload_download(
156156
mock_io_log_redirect_cb: LogRedirectCB,
157157
):
158158
async with ProgressBarData(steps=2) as progress_bar:
159-
await data_manager._push_directory(
159+
await data_manager._push_directory( # noqa: SLF001
160160
user_id=user_id,
161161
project_id=project_id,
162162
node_uuid=node_uuid,
@@ -166,13 +166,15 @@ async def test_valid_upload_download(
166166
r_clone_settings=r_clone_settings,
167167
)
168168
# pylint: disable=protected-access
169-
assert progress_bar._continuous_progress_value == pytest.approx(1.0)
169+
assert progress_bar._continuous_progress_value == pytest.approx( # noqa: SLF001
170+
1.0
171+
)
170172

171173
uploaded_hashes = _get_file_hashes_in_path(content_path)
172174

173175
_empty_path(content_path)
174176

175-
await data_manager._pull_directory(
177+
await data_manager._pull_directory( # noqa: SLF001
176178
user_id=user_id,
177179
project_id=project_id,
178180
node_uuid=node_uuid,
@@ -181,7 +183,9 @@ async def test_valid_upload_download(
181183
r_clone_settings=r_clone_settings,
182184
progress_bar=progress_bar,
183185
)
184-
assert progress_bar._continuous_progress_value == pytest.approx(2.0)
186+
assert progress_bar._continuous_progress_value == pytest.approx( # noqa: SLF001
187+
2.0
188+
)
185189

186190
downloaded_hashes = _get_file_hashes_in_path(content_path)
187191

@@ -207,7 +211,7 @@ async def test_valid_upload_download_saved_to(
207211
mock_io_log_redirect_cb: LogRedirectCB,
208212
):
209213
async with ProgressBarData(steps=2) as progress_bar:
210-
await data_manager._push_directory(
214+
await data_manager._push_directory( # noqa: SLF001
211215
user_id=user_id,
212216
project_id=project_id,
213217
node_uuid=node_uuid,
@@ -227,7 +231,7 @@ async def test_valid_upload_download_saved_to(
227231

228232
new_destination = random_tmp_dir_generator(is_file=content_path.is_file())
229233

230-
await data_manager._pull_directory(
234+
await data_manager._pull_directory( # noqa: SLF001
231235
user_id=user_id,
232236
project_id=project_id,
233237
node_uuid=node_uuid,
@@ -290,7 +294,7 @@ async def test_delete_legacy_archive(
290294
)
291295

292296
assert (
293-
await data_manager._state_metadata_entry_exists(
297+
await data_manager._state_metadata_entry_exists( # noqa: SLF001
294298
user_id=user_id,
295299
project_id=project_id,
296300
node_uuid=node_uuid,
@@ -300,15 +304,14 @@ async def test_delete_legacy_archive(
300304
is True
301305
)
302306

303-
await data_manager._delete_legacy_archive(
304-
user_id=user_id,
307+
await data_manager._delete_legacy_archive( # noqa: SLF001
305308
project_id=project_id,
306309
node_uuid=node_uuid,
307310
path=content_path,
308311
)
309312

310313
assert (
311-
await data_manager._state_metadata_entry_exists(
314+
await data_manager._state_metadata_entry_exists( # noqa: SLF001
312315
user_id=user_id,
313316
project_id=project_id,
314317
node_uuid=node_uuid,

0 commit comments

Comments
 (0)