Skip to content

Commit 3b8439d

Browse files
committed
lots of changes, sort out.
1 parent 6615b8b commit 3b8439d

5 files changed

Lines changed: 158 additions & 116 deletions

File tree

tests/conftest.py

Lines changed: 0 additions & 46 deletions
This file was deleted.

tests/ssh_test_images/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Use a base image with the desired OS (e.g., Ubuntu, Debian, etc.)
2+
FROM ubuntu:latest
3+
# Install SSH server
4+
RUN apt-get update && \
5+
apt-get install -y openssh-server
6+
RUN apt-get install nano
7+
# Create an SSH user
8+
RUN useradd -rm -d /home/sshuser -s /bin/bash -g root -G sudo -u 1000 sshuser
9+
# Set the SSH user's password (replace "password" with your desired password)
10+
RUN echo 'sshuser:password' | chpasswd
11+
# Allow SSH access
12+
RUN mkdir /var/run/sshd
13+
# Expose the SSH port
14+
EXPOSE 22
15+
# Start SSH server on container startup
16+
CMD ["/usr/sbin/sshd", "-D"]

tests/ssh_test_utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,29 @@ def setup_hostkeys(project):
5555
project.cfg["central_host_id"], project.cfg.hostkeys_path, log=True
5656
)
5757
restore_mock_input(orig_builtin)
58+
59+
orig_getpass = copy.deepcopy(ssh.getpass.getpass)
60+
ssh.getpass.getpass = lambda _: "password"
61+
62+
ssh.setup_ssh_key(project.cfg, log=False)
63+
ssh.getpass.getpass = orig_getpass
64+
65+
66+
def build_docker_image(project):
67+
import os
68+
import subprocess
69+
from pathlib import Path
70+
71+
image_path = Path(__file__).parent / "ssh_test_images"
72+
os.chdir(image_path)
73+
subprocess.run("docker build -t ssh_server .", shell=True)
74+
subprocess.run(
75+
"docker run -d -p 22:22 ssh_server", shell=True
76+
) # ; docker build -t ssh_server .", shell=True) # ;docker run -p 22:22 ssh_server
77+
78+
setup_project_for_ssh(
79+
project,
80+
central_path=f"/home/sshuser/datashuttle/{project.project_name}",
81+
central_host_id="localhost",
82+
central_host_username="sshuser",
83+
)

tests/tests_integration/test_ssh_file_transfer.py

Lines changed: 112 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,36 @@
11
"""
22
"""
33
import copy
4-
import glob
54
import shutil
6-
import time
5+
import stat
76
from pathlib import Path
87

98
import pandas as pd
9+
import paramiko
1010
import pytest
1111
import ssh_test_utils
1212
import test_utils
1313
from file_conflicts_pathtable import get_pathtable
14-
from pytest import ssh_config
14+
#from pytest import ssh_config
1515

16+
from datashuttle.utils import ssh
17+
18+
TEST_SSH = True # TODO: base on whether docker / singularity is installed.
1619

1720
class TestFileTransfer:
1821
@pytest.fixture(
1922
scope="class",
20-
params=[ # Set running SSH or local filesystem (see docstring).
21-
False,
23+
params=[
24+
# False,
2225
pytest.param(
2326
True,
2427
marks=pytest.mark.skipif(
25-
ssh_config.TEST_SSH is False,
26-
reason="TEST_SSH is set to False.",
28+
TEST_SSH is False, reason="TEST_SSH is set to False."
2729
),
2830
),
2931
],
3032
)
31-
def pathtable_and_project(self, request, tmpdir_factory):
33+
def project_and_test_information(self, request, tmpdir_factory):
3234
"""
3335
Create a project for SSH testing. Setup
3436
the project as normal, and switch configs
@@ -72,44 +74,24 @@ def pathtable_and_project(self, request, tmpdir_factory):
7274
testing_ssh = request.param
7375
tmp_path = tmpdir_factory.mktemp("test")
7476

75-
if testing_ssh:
76-
base_path = ssh_config.FILESYSTEM_PATH
77-
central_path = ssh_config.SERVER_PATH
78-
else:
79-
base_path = tmp_path / "test with space"
80-
central_path = base_path
77+
base_path = tmp_path / "test with space"
8178
test_project_name = "test_file_conflicts"
8279

8380
project, cwd = test_utils.setup_project_fixture(
8481
base_path, test_project_name
8582
)
8683

8784
if testing_ssh:
88-
ssh_test_utils.setup_project_for_ssh(
89-
project,
90-
test_utils.make_test_path(
91-
central_path, test_project_name, "central"
92-
),
93-
ssh_config.CENTRAL_HOST_ID,
94-
ssh_config.USERNAME,
95-
)
96-
97-
# Initialise the SSH connection
85+
ssh_test_utils.build_docker_image(project)
9886
ssh_test_utils.setup_hostkeys(project)
99-
shutil.copy(ssh_config.SSH_KEY_PATH, project.cfg.file_path.parent)
10087

10188
pathtable = get_pathtable(project.cfg["local_path"])
10289
self.create_all_pathtable_files(pathtable)
103-
project.testing_ssh = testing_ssh
10490

105-
yield [pathtable, project]
91+
yield [pathtable, project, testing_ssh]
10692

10793
test_utils.teardown_project(cwd, project)
10894

109-
if testing_ssh:
110-
for result in glob.glob(ssh_config.FILESYSTEM_PATH):
111-
shutil.rmtree(result)
112-
11395
# -------------------------------------------------------------------------
11496
# Utils
11597
# -------------------------------------------------------------------------
@@ -156,14 +138,14 @@ def central_from_local(self, path_):
156138
["histology", "behav", "all_ses_level_non_datatype"],
157139
],
158140
)
159-
@pytest.mark.parametrize("upload_or_download", ["upload", "download"])
141+
# @pytest.mark.parametrize("upload_or_download", ["upload", "download"])
160142
def test_all_data_transfer_options(
161143
self,
162-
pathtable_and_project,
144+
project_and_test_information,
163145
sub_names,
164146
ses_names,
165147
datatype,
166-
upload_or_download,
148+
# upload_or_download,
167149
):
168150
"""
169151
Parse the arguments to filter the pathtable, getting
@@ -175,31 +157,31 @@ def test_all_data_transfer_options(
175157
on setting up and swapping local / central paths for
176158
upload / download tests.
177159
"""
178-
pathtable, project = pathtable_and_project
160+
pathtable, project, testing_ssh = project_and_test_information
179161

180-
transfer_function = test_utils.handle_upload_or_download(
181-
project,
182-
upload_or_download,
183-
swap_last_folder_only=project.testing_ssh,
184-
)[0]
162+
# transfer_function = test_utils.handle_upload_or_download(
163+
# project,
164+
# upload_or_download,
165+
# swap_last_folder_only=testing_ssh,
166+
# )[0]
185167

186-
transfer_function(sub_names, ses_names, datatype, init_log=False)
168+
project.upload(sub_names, ses_names, datatype, init_log=False)
169+
# transfer_function(sub_names, ses_names, datatype, init_log=False)
187170

188-
if upload_or_download == "download":
189-
test_utils.swap_local_and_central_paths(
190-
project, swap_last_folder_only=project.testing_ssh
191-
)
171+
# if upload_or_download == "download":
172+
# test_utils.swap_local_and_central_paths(
173+
# project, swap_last_folder_only=testing_ssh
174+
# )
192175

193-
sub_names = self.parse_arguments(pathtable, sub_names, "sub")
194-
ses_names = self.parse_arguments(pathtable, ses_names, "ses")
195-
datatype = self.parse_arguments(pathtable, datatype, "datatype")
176+
parsed_sub_names = self.parse_arguments(pathtable, sub_names, "sub")
177+
parsed_ses_names = self.parse_arguments(pathtable, ses_names, "ses")
178+
parsed_datatype = self.parse_arguments(pathtable, datatype, "datatype")
196179

197-
# Filter pathtable to get files that were expected
198-
# to be transferred
180+
# Filter pathtable to get files that were expected to be transferred
199181
(
200182
sub_ses_dtype_arguments,
201183
extra_arguments,
202-
) = self.make_pathtable_search_filter(sub_names, ses_names, datatype)
184+
) = self.make_pathtable_search_filter(parsed_sub_names, parsed_ses_names, parsed_datatype)
203185

204186
datatype_folders = self.query_table(pathtable, sub_ses_dtype_arguments)
205187
extra_folders = self.query_table(pathtable, extra_arguments)
@@ -214,28 +196,90 @@ def test_all_data_transfer_options(
214196

215197
# When transferring with SSH, there is a delay before
216198
# filesystem catches up
217-
if project.testing_ssh:
218-
time.sleep(0.5)
199+
# if testing_ssh:
200+
# time.sleep(0.5)
219201

220202
# Check what paths were actually moved
221203
# (through the local filesystem), and test
222-
path_to_search = (
223-
self.central_from_local(project.cfg["local_path"]) / "rawdata"
224-
)
225-
all_transferred = path_to_search.glob("**/*")
226-
paths_to_transferred_files = list(
227-
filter(Path.is_file, all_transferred)
228-
)
204+
def sftp_recursive_search(sftp, path_, all_filenames):
205+
try:
206+
sftp.stat(path_)
207+
except FileNotFoundError:
208+
return
209+
210+
for file_or_folder in sftp.listdir_attr(path_):
211+
if stat.S_ISDIR(file_or_folder.st_mode):
212+
sftp_recursive_search(
213+
sftp,
214+
path_ + "/" + file_or_folder.filename,
215+
all_filenames,
216+
)
217+
else:
218+
all_filenames.append(path_ + "/" + file_or_folder.filename)
219+
220+
with paramiko.SSHClient() as client:
221+
ssh.connect_client(client, project.cfg)
222+
223+
sftp = client.open_sftp()
224+
225+
all_filenames = []
226+
227+
sftp_recursive_search(
228+
sftp,
229+
(project.cfg["central_path"] / "rawdata").as_posix(),
230+
all_filenames,
231+
)
229232

230-
assert sorted(paths_to_transferred_files) == sorted(
231-
expected_transferred_paths
232-
)
233+
paths_to_transferred_files = []
234+
for path_ in all_filenames:
235+
parts = Path(path_).parts
236+
paths_to_transferred_files.append(
237+
Path(*parts[parts.index("rawdata") :])
238+
)
239+
240+
expected_transferred_paths_ = []
241+
for path_ in expected_transferred_paths:
242+
parts = Path(path_).parts
243+
expected_transferred_paths_.append(
244+
Path(*parts[parts.index("rawdata") :])
245+
)
246+
247+
assert sorted(paths_to_transferred_files) == sorted(
248+
expected_transferred_paths_
249+
)
250+
251+
project.upload_all()
252+
shutil.rmtree(project.cfg["local_path"] / "rawdata") # TOOD: var
253+
254+
breakpoint()
255+
256+
true_local_path = project.cfg["local_path"]
257+
tmp_local_path = project.cfg["local_path"] / "tmp_local"
258+
tmp_local_path.mkdirs()
259+
project.update_config("local_path", tmp_local_path)
260+
261+
project.download(sub_names, ses_names, datatype, init_log=False) # TODO: why is this connecting so many times?
262+
263+
all_transferred = list((project.cfg["local_path"] / "rawdata").glob("**/*"))
264+
all_transferred = [path_ for path_ in all_transferred if path_.is_file()]
265+
266+
paths_to_transferred_files = []
267+
for path_ in all_transferred: # TODO: rename all filenames
268+
parts = Path(path_).parts
269+
paths_to_transferred_files.append(
270+
Path(*parts[parts.index("rawdata"):])
271+
)
272+
273+
assert sorted(paths_to_transferred_files) == sorted(expected_transferred_paths_)
274+
275+
shutil.rmtree(project.cfg["local_path"]) # TOOD: var
276+
277+
project.update_config("local_path", true_local_path)
278+
279+
with paramiko.SSHClient() as client:
280+
ssh.connect_client(client, project.cfg)
233281

234-
# Teardown here, because we have session scope.
235-
try:
236-
shutil.rmtree(self.central_from_local(project.cfg["local_path"]))
237-
except FileNotFoundError:
238-
pass
282+
client.exec_command(f"rm -rf {(project.cfg['central_path'] / 'rawdata').as_posix()}") # TODO: own function as need to do on teardown)
239283

240284
# ---------------------------------------------------------------------------------------------------------------
241285
# Utils

tests/tests_integration/test_ssh_setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
import pytest
77
import ssh_test_utils
88
import test_utils
9-
from pytest import ssh_config
109

10+
# from pytest import ssh_config
1111
from datashuttle.utils import ssh
1212

13+
TEST_SSH = False
1314

14-
@pytest.mark.skipif(ssh_config.TEST_SSH is False, reason="TEST_SSH is false")
15+
16+
@pytest.mark.skipif(TEST_SSH is False, reason="TEST_SSH is false")
1517
class TestSSH:
1618
@pytest.fixture(scope="function")
1719
def project(test, tmp_path):

0 commit comments

Comments
 (0)