diff --git a/boss/api/ssh.py b/boss/api/ssh.py index 47d7563..94bf1ed 100644 --- a/boss/api/ssh.py +++ b/boss/api/ssh.py @@ -1,7 +1,12 @@ ''' SSH module based on paramiko. ''' +import os +from time import time +from tempfile import mkdtemp + from boss import state from boss.core import remote +from boss.core.fs import compress def run(command, **params): @@ -161,3 +166,31 @@ def write(remote_path, data, **params): callback=params.get('callback'), confirm=params.get('confirm') ) + + +def upload_dir(local_path, remote_path, callback=None): + ''' Upload local directory to the remote. ''' + tmp_folder = mkdtemp() + tar_filename = os.path.basename(local_path) + '.tar.gz' + tar_path = os.path.join(tmp_folder, tar_filename) + + # Compress the directory. + compress(local_path, tar_path) + + # Upload the tar zipped file to the remote. + # The compressed folder gets uploaded to a temp path first. + # Then later is extracted to the provided path on the remote. + remote_tmp_path = '/tmp/upload-' + str(time()).replace('.', '-') + put(tar_path, remote_tmp_path, callback) + + # Extract the files to the remote directory + run('mkdir -p {}'.format(remote_path)) + run( + 'tar zxvf {src} --strip-components=1 -C {dest}'.format( + src=remote_tmp_path, + dest=remote_path + ) + ) + run('rm -rf {}'.format(remote_tmp_path)) + + os.remove(tar_path) diff --git a/tests/integration/test_ssh_integration.py b/tests/integration/test_ssh_integration.py new file mode 100644 index 0000000..6bccfc3 --- /dev/null +++ b/tests/integration/test_ssh_integration.py @@ -0,0 +1,42 @@ +''' Integration tests for ssh module. ''' + + +import os +from tempfile import mkdtemp +from mock import patch + +from boss.core import fs +from boss.api.ssh import upload_dir + + +def test_upload_dir(server): + ''' Test upload_dir() transfers a local directory to the remote end. ''' + + # Setup local directory. + local_dir = os.path.join(mkdtemp(), 'test') + + os.mkdir(local_dir) + os.mkdir(os.path.join(local_dir, 'a')) + os.mkdir(os.path.join(local_dir, 'b')) + + fs.write(os.path.join(local_dir, 'a/foo.txt'), 'Foo') + fs.write(os.path.join(local_dir, 'b/bar.txt'), 'Bar') + + for uid in server.users: + remote_path = os.path.join(server.ROOT_DIR, 'remote') + + with server.client(uid) as client: + with patch('boss.api.ssh.resolve_client') as rc_m: + rc_m.return_value = client + + assert not fs.exists(remote_path) + + # Upload the directory + upload_dir(local_dir, remote_path) + + # Test the whole directory got uploaded with all files + assert fs.exists(remote_path) + assert fs.exists(os.path.join(remote_path, 'a')) + assert fs.exists(os.path.join(remote_path, 'b')) + assert fs.read(os.path.join(remote_path, 'a/foo.txt')) == 'Foo' + assert fs.read(os.path.join(remote_path, 'b/bar.txt')) == 'Bar'