Skip to content

Commit ac653e5

Browse files
authored
🐛 Storage: Wrong file destination path when copying a project (ITISFoundation#3700)
1 parent 0890bfe commit ac653e5

File tree

6 files changed

+63
-19
lines changed

6 files changed

+63
-19
lines changed

services/storage/src/simcore_service_storage/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def from_simcore_node(
9999
"location": location_name,
100100
"bucket_name": bucket,
101101
"object_name": file_id,
102-
"file_name": parts[2],
102+
"file_name": parts[-1],
103103
"user_id": user_id,
104104
"project_id": parse_obj_as(ProjectID, parts[0])
105105
if is_uuid(parts[0])

services/storage/src/simcore_service_storage/simcore_s3_dsm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ async def deep_copy_project_simcore_s3(
470470
user_id,
471471
src_fmd,
472472
SimcoreS3FileID(
473-
f"{dst_project_uuid}/{new_node_id}/{src_fmd.object_name.split('/')[-1]}"
473+
f"{dst_project_uuid}/{new_node_id}/{src_fmd.object_name.split('/', maxsplit=2)[-1]}"
474474
),
475475
bytes_transfered_cb=s3_transfered_data_cb.copy_transfer_cb,
476476
)

services/storage/tests/conftest.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,10 +543,19 @@ async def _uploader(
543543

544544

545545
@pytest.fixture
546-
def create_simcore_file_id() -> Callable[[ProjectID, NodeID, str], SimcoreS3FileID]:
546+
def create_simcore_file_id(
547+
faker: Faker,
548+
) -> Callable[[ProjectID, NodeID, str, Optional[Path]], SimcoreS3FileID]:
547549
def _creator(
548-
project_id: ProjectID, node_id: NodeID, file_name: str
550+
project_id: ProjectID,
551+
node_id: NodeID,
552+
file_name: str,
553+
file_base_path: Optional[Path] = None,
549554
) -> SimcoreS3FileID:
550-
return parse_obj_as(SimcoreS3FileID, f"{project_id}/{node_id}/{file_name}")
555+
s3_file_name = file_name
556+
if file_base_path:
557+
s3_file_name = f"{file_base_path / file_name}"
558+
clean_path = Path(f"{project_id}/{node_id}/{s3_file_name}")
559+
return SimcoreS3FileID(f"{clean_path}")
551560

552561
return _creator

services/storage/tests/fixtures/data_models.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# pylint:disable=redefined-outer-name
44

55

6-
from typing import Any, AsyncIterator, Awaitable, Callable
6+
from typing import Any, AsyncIterator, Awaitable, Callable, Optional
77

88
import pytest
99
import sqlalchemy as sa
@@ -79,7 +79,9 @@ async def project_id(
7979
async def create_project_node(
8080
user_id: UserID, aiopg_engine: Engine, faker: Faker
8181
) -> AsyncIterator[Callable[..., Awaitable[NodeID]]]:
82-
async def _creator(project_id: ProjectID, **kwargs) -> NodeID:
82+
async def _creator(
83+
project_id: ProjectID, node_id: Optional[NodeID] = None, **kwargs
84+
) -> NodeID:
8385
async with aiopg_engine.acquire() as conn:
8486
result = await conn.execute(
8587
sa.select([projects.c.workbench]).where(
@@ -89,7 +91,7 @@ async def _creator(project_id: ProjectID, **kwargs) -> NodeID:
8991
row = await result.fetchone()
9092
assert row
9193
project_workbench: dict[str, Any] = row[projects.c.workbench]
92-
new_node_id = NodeID(faker.uuid4())
94+
new_node_id = node_id or NodeID(faker.uuid4())
9395
node_data = {
9496
"key": "simcore/services/frontend/file-picker",
9597
"version": "1.0.0",

services/storage/tests/helpers/utils_file_meta_data.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ async def assert_file_meta_data_in_db(
2424
)
2525
db_data = await result.fetchall()
2626
assert db_data is not None
27-
assert len(db_data) == (1 if expected_entry_exists else 0)
27+
assert len(db_data) == (1 if expected_entry_exists else 0), (
28+
f"{file_id} was not found!"
29+
if expected_entry_exists
30+
else f"{file_id} should not exist"
31+
)
2832
upload_id = None
2933
if expected_entry_exists:
3034
row = db_data[0]

services/storage/tests/unit/test_handlers_simcore_s3.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ async def random_project_with_files(
186186
aiopg_engine: Engine,
187187
create_project: Callable[[], Awaitable[dict[str, Any]]],
188188
create_project_node: Callable[..., Awaitable[NodeID]],
189-
create_simcore_file_id: Callable[[ProjectID, NodeID, str], SimcoreS3FileID],
189+
create_simcore_file_id: Callable[
190+
[ProjectID, NodeID, str, Optional[Path]], SimcoreS3FileID
191+
],
190192
upload_file: Callable[
191193
[ByteSize, str, str], Awaitable[tuple[Path, SimcoreS3FileID]]
192194
],
@@ -207,23 +209,43 @@ async def _creator(
207209
src_projects_list: dict[NodeID, dict[SimcoreS3FileID, Path]] = {}
208210
upload_tasks: deque[Awaitable] = deque()
209211
for _node_index in range(num_nodes):
210-
# NOTE: we put some more outputs in there to simuate a real case better
212+
# NOTE: we put some more outputs in there to simulate a real case better
213+
new_node_id = NodeID(faker.uuid4())
214+
output3_file_id = create_simcore_file_id(
215+
ProjectID(project["uuid"]),
216+
new_node_id,
217+
faker.file_name(),
218+
Path("outputs/output3"),
219+
)
211220
src_node_id = await create_project_node(
212221
ProjectID(project["uuid"]),
213-
outputs={"output_1": faker.pyint(), "output_2": faker.pystr()},
222+
new_node_id,
223+
outputs={
224+
"output_1": faker.pyint(),
225+
"output_2": faker.pystr(),
226+
"output_3": f"{output3_file_id}",
227+
},
214228
)
229+
assert src_node_id == new_node_id
230+
231+
# upload the output 3 and some random other files at the root of each node
215232
src_projects_list[src_node_id] = {}
233+
src_file, _ = await upload_file(
234+
choice(file_sizes), Path(output3_file_id).name, output3_file_id
235+
)
236+
src_projects_list[src_node_id][output3_file_id] = src_file
216237

217238
async def _upload_file_and_update_project(project, src_node_id):
218239
src_file_name = faker.file_name()
219240
src_file_uuid = create_simcore_file_id(
220-
ProjectID(project["uuid"]), src_node_id, src_file_name
241+
ProjectID(project["uuid"]), src_node_id, src_file_name, None
221242
)
222243
src_file, _ = await upload_file(
223244
choice(file_sizes), src_file_name, src_file_uuid
224245
)
225246
src_projects_list[src_node_id][src_file_uuid] = src_file
226247

248+
# add a few random files in the node storage
227249
upload_tasks.extend(
228250
[
229251
_upload_file_and_update_project(project, src_node_id)
@@ -277,11 +299,14 @@ async def test_copy_folders_from_valid_project_with_one_large_file(
277299
for src_node_id in src_projects_list:
278300
dst_node_id = nodes_map.get(NodeIDStr(f"{src_node_id}"))
279301
assert dst_node_id
280-
for src_file in src_projects_list[src_node_id].values():
302+
for src_file_id, src_file in src_projects_list[src_node_id].items():
281303
await assert_file_meta_data_in_db(
282304
aiopg_engine,
283-
file_id=create_simcore_file_id(
284-
ProjectID(dst_project["uuid"]), NodeID(dst_node_id), src_file.name
305+
file_id=parse_obj_as(
306+
SimcoreS3FileID,
307+
f"{src_file_id}".replace(
308+
src_project["uuid"], dst_project["uuid"]
309+
).replace(f"{src_node_id}", f"{dst_node_id}"),
285310
),
286311
expected_entry_exists=True,
287312
expected_file_size=src_file.stat().st_size,
@@ -317,15 +342,19 @@ async def test_copy_folders_from_valid_project(
317342
assert data == jsonable_encoder(
318343
await _get_updated_project(aiopg_engine, dst_project["uuid"])
319344
)
345+
320346
# check that file meta data was effectively copied
321347
for src_node_id in src_projects_list:
322348
dst_node_id = nodes_map.get(NodeIDStr(f"{src_node_id}"))
323349
assert dst_node_id
324-
for src_file in src_projects_list[src_node_id].values():
350+
for src_file_id, src_file in src_projects_list[src_node_id].items():
325351
await assert_file_meta_data_in_db(
326352
aiopg_engine,
327-
file_id=create_simcore_file_id(
328-
ProjectID(dst_project["uuid"]), NodeID(dst_node_id), src_file.name
353+
file_id=parse_obj_as(
354+
SimcoreS3FileID,
355+
f"{src_file_id}".replace(
356+
src_project["uuid"], dst_project["uuid"]
357+
).replace(f"{src_node_id}", f"{dst_node_id}"),
329358
),
330359
expected_entry_exists=True,
331360
expected_file_size=src_file.stat().st_size,

0 commit comments

Comments
 (0)