def test_cli_args(): """Test CLI arguments.""" # Suppress STDERR with capture_sys_output(): assert_raises(SystemExit, _sync_argv, []) assert_raises(SystemExit, _sync_argv, [LOCAL_FOLDER]) with override_env_variables(): _sync_argv( [LOCAL_FOLDER, '127.0.0.1:' + '/' + REMOTE_FOLDER, '-f', '-k', t_path("id_rsa"), '-p', "2222", '-d', '-c', '/dev/null' ], ) _sync_argv( [LOCAL_FOLDER, '[email protected]:' + '/' + REMOTE_FOLDER, '-f', '-k', t_path("id_rsa"), '-p', "2222", '-d', '-c', '/dev/null' ], ) _sync_argv( [LOCAL_FOLDER, 'test:[email protected]:' + '/' + REMOTE_FOLDER, '-p', "2222", '-d' ], ) _sync_argv( [LOCAL_FOLDER, 'test:[email protected]:' + '/' + REMOTE_FOLDER, '-p', "2222", '-n', t_path("known_hosts") ], ) _sync_argv( [LOCAL_FOLDER, 'backup:' + '/' + REMOTE_FOLDER, '-c', t_path("config"), # hard to insert relative path in cfg, so we have to cheat '-k', t_path("id_rsa"), '-d' ], )
def _start_sftp_server(): """Start the SFTP local server.""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setblocking(0) sock.bind(('localhost', 2222)) sock.listen(10) reads = {sock} others = set() while not event.is_set(): ready_to_read, _, _ = \ select.select( reads, others, others, 1) if sock in ready_to_read: client_socket, address = sock.accept() ts = paramiko.Transport(client_socket) host_key = paramiko.RSAKey.from_private_key_file( t_path('server_id_rsa')) ts.add_server_key(host_key) server = StubServer() ts.set_subsystem_handler('sftp', paramiko.SFTPServer, StubSFTPServer) ts.start_server(server=server) sock.close()
def _sync(password=False, fix=False, exclude=None, ssh_agent=False, delete=True): """Launch sync and do basic comparison of dir trees.""" if not password: remote = '[email protected]:' + '/' + REMOTE_FOLDER else: remote = 'test:[email protected]:' + '/' + REMOTE_FOLDER sync = SFTPClone(LOCAL_FOLDER, remote, port=2222, fix_symlinks=fix, identity_files=[t_path("id_rsa")], exclude_file=exclude, ssh_agent=ssh_agent, delete=delete) sync.run() if not exclude and delete: # check the directory trees assert \ file_tree( LOCAL_FOLDER )[LOCAL_FOLDER_NAME] == file_tree( REMOTE_PATH )[REMOTE_FOLDER]
def _start_sftp_server(): """Start the SFTP local server.""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setblocking(0) sock.bind(('localhost', 2222)) sock.listen(10) reads = {sock} others = set() while not event.is_set(): ready_to_read, _, _ = \ select.select( reads, others, others, 1) if sock in ready_to_read: client_socket, address = sock.accept() ts = paramiko.Transport(client_socket) host_key = paramiko.RSAKey.from_private_key_file( t_path('server_id_rsa') ) ts.add_server_key(host_key) server = StubServer() ts.set_subsystem_handler( 'sftp', paramiko.SFTPServer, StubSFTPServer) ts.start_server(server=server) sock.close()
def _sync( password=False, fix=False, exclude=None, ssh_agent=False, delete=True ): """Launch sync and do basic comparison of dir trees.""" if not password: remote = '[email protected]:' + '/' + REMOTE_FOLDER else: remote = 'test:[email protected]:' + '/' + REMOTE_FOLDER sync = SFTPClone( LOCAL_FOLDER, remote, port=2222, fix_symlinks=fix, identity_files=[t_path("id_rsa")], exclude_file=exclude, ssh_agent=ssh_agent, delete=delete ) sync.run() if not exclude and delete: # check the directory trees assert \ file_tree( LOCAL_FOLDER )[LOCAL_FOLDER_NAME] == file_tree( REMOTE_PATH )[REMOTE_FOLDER]
def test_inner_exclude(): """Test pattern exclusion (with recursion) handling.""" os.mkdir(join(LOCAL_FOLDER, "bar")) os.mkdir(join(LOCAL_FOLDER, "bar", "inner")) os.open(join(LOCAL_FOLDER, "bar", "file_one"), os.O_CREAT) os.open(join(LOCAL_FOLDER, "bar", "inner", "foo"), os.O_CREAT) os.open(join(LOCAL_FOLDER, "bar", "inner", "bar"), os.O_CREAT) _sync(exclude=t_path("exclude")) assert set(os.listdir(join(REMOTE_PATH, "bar"))) == {"file_one", "inner"} eq_(set(os.listdir(join(REMOTE_PATH, "bar", "inner"))), {"bar"})
def test_create_remote_directory(): """Test create a remote folder.""" os.mkdir(LOCAL_FOLDER) os.mkdir(join(LOCAL_FOLDER, "foofolder")) _sync_argv([ LOCAL_FOLDER, 'test:[email protected]:' + '/' + REMOTE_FOLDER, '-p', "2222", '-n', t_path("known_hosts"), '-r' ]) assert (os.listdir(REMOTE_PATH) == os.listdir(LOCAL_FOLDER))
def test_exclude(): """Test pattern exclusion handling.""" excluded = {"foofolder"} os.mkdir(join(LOCAL_FOLDER, "foofolder")) excluded |= {"foo", "foofile"} os.open(join(LOCAL_FOLDER, "file_one"), os.O_CREAT) os.open(join(LOCAL_FOLDER, "file_two"), os.O_CREAT) os.open(join(LOCAL_FOLDER, "foo"), os.O_CREAT) os.open(join(LOCAL_FOLDER, "foofile"), os.O_CREAT) _sync(exclude=t_path("exclude")) assert not set(os.listdir(REMOTE_PATH)) & excluded
def test_remote_tilde_home(): """Test tilde expansion on remote end.""" normal_files = ("bar", "bis") # just to add noise for f in normal_files: os.open(join(LOCAL_FOLDER, f), os.O_CREAT) os.open(join(REMOTE_PATH, f), os.O_CREAT) sync = SFTPClone( LOCAL_FOLDER, remote_url='[email protected]:' + '~' + REMOTE_FOLDER, port=2222, identity_files=[t_path("id_rsa"), ] ) sync.run() assert file_tree(LOCAL_FOLDER)[LOCAL_FOLDER_NAME] == file_tree(REMOTE_PATH)[REMOTE_FOLDER]
import paramiko import socket import select from nose import with_setup from nose.tools import assert_raises, raises, eq_ from contextlib import contextmanager import sys # unicode / string differentiation if sys.version_info > (3, 0): from io import StringIO else: from StringIO import StringIO REMOTE_ROOT = t_path("server_root") REMOTE_FOLDER = "server_folder" REMOTE_PATH = join(REMOTE_ROOT, REMOTE_FOLDER) LOCAL_FOLDER_NAME = "local_folder" LOCAL_FOLDER = t_path(LOCAL_FOLDER_NAME) _u = functools.partial(unicodedata.normalize, "NFKD") event = threading.Event() # attach existing loggers (use --nologcapture option to see output) logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
import paramiko import socket import select from nose import with_setup from nose.tools import assert_raises, raises, eq_ from contextlib import contextmanager import sys # unicode / string differentiation if sys.version_info > (3, 0): from io import StringIO else: from StringIO import StringIO REMOTE_ROOT = t_path("server_root") REMOTE_FOLDER = "server_folder" REMOTE_PATH = join(REMOTE_ROOT, REMOTE_FOLDER) LOCAL_FOLDER_NAME = "local_folder" LOCAL_FOLDER = t_path(LOCAL_FOLDER_NAME) _u = functools.partial(unicodedata.normalize, "NFKD") event = threading.Event() # attach existing loggers (use --nologcapture option to see output) logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s' )
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. """ A stub SFTP server for loopback SFTP testing. """ import os from paramiko import ServerInterface, SFTPServerInterface, SFTPServer, SFTPAttributes, \ SFTPHandle, SFTP_OK, AUTH_SUCCESSFUL, AUTH_FAILED, OPEN_SUCCEEDED, RSAKey from paramiko.common import o666 from sftpclone.t.utils import t_path USERNAME = "******" PASSWORD = "******" RSA_KEY = t_path("id_rsa") SERVER_ROOT = "server_root" class StubServer (ServerInterface): good_pub_key = RSAKey(filename=RSA_KEY) def check_auth_password(self, username, password): if username == USERNAME and password == PASSWORD: return AUTH_SUCCESSFUL return AUTH_FAILED def check_auth_publickey(self, username, key): if username == USERNAME and key == self.good_pub_key:
class StubSFTPServer (SFTPServerInterface): ROOT = t_path(SERVER_ROOT) def _realpath(self, path): return self.ROOT + self.canonicalize(path) def list_folder(self, path): path = self._realpath(path) try: out = [] flist = os.listdir(path) for fname in flist: attr = SFTPAttributes.from_stat( os.lstat(os.path.join(path, fname)) ) attr.filename = fname.encode("utf-8") out.append(attr) return out except OSError as e: return SFTPServer.convert_errno(e.errno) def stat(self, path): path = self._realpath(path) try: return SFTPAttributes.from_stat(os.stat(path)) except OSError as e: return SFTPServer.convert_errno(e.errno) def lstat(self, path): path = self._realpath(path) try: return SFTPAttributes.from_stat(os.lstat(path)) except OSError as e: return SFTPServer.convert_errno(e.errno) def open(self, path, flags, attr): path = self._realpath(path) try: binary_flag = getattr(os, 'O_BINARY', 0) flags |= binary_flag mode = getattr(attr, 'st_mode', None) if mode is not None: fd = os.open(path, flags, mode) else: # os.open() defaults to 0777 which is # an odd default mode for files fd = os.open(path, flags, o666) except OSError as e: return SFTPServer.convert_errno(e.errno) if (flags & os.O_CREAT) and (attr is not None): attr._flags &= ~attr.FLAG_PERMISSIONS SFTPServer.set_file_attr(path, attr) if flags & os.O_WRONLY: if flags & os.O_APPEND: fstr = 'ab' else: fstr = 'wb' elif flags & os.O_RDWR: if flags & os.O_APPEND: fstr = 'a+b' else: fstr = 'r+b' else: # O_RDONLY (== 0) fstr = 'rb' try: f = os.fdopen(fd, fstr) except OSError as e: return SFTPServer.convert_errno(e.errno) fobj = StubSFTPHandle(flags) fobj.filename = path fobj.readfile = f fobj.writefile = f return fobj def remove(self, path): path = self._realpath(path) try: os.remove(path) except OSError as e: return SFTPServer.convert_errno(e.errno) return SFTP_OK def rename(self, oldpath, newpath): oldpath = self._realpath(oldpath) newpath = self._realpath(newpath) try: os.rename(oldpath, newpath) except OSError as e: return SFTPServer.convert_errno(e.errno) return SFTP_OK def mkdir(self, path, attr): path = self._realpath(path) try: os.mkdir(path) if attr is not None: SFTPServer.set_file_attr(path, attr) except OSError as e: return SFTPServer.convert_errno(e.errno) return SFTP_OK def rmdir(self, path): path = self._realpath(path) try: os.rmdir(path) except OSError as e: return SFTPServer.convert_errno(e.errno) return SFTP_OK def chattr(self, path, attr): path = self._realpath(path) try: SFTPServer.set_file_attr(path, attr) except OSError as e: return SFTPServer.convert_errno(e.errno) return SFTP_OK def symlink(self, target_path, path): path = self._realpath(path) if (len(target_path) > 0) and (target_path[0] == '/'): # absolute symlink target_path = os.path.join(self.ROOT, target_path[1:]) try: os.symlink(target_path, path) except OSError as e: return SFTPServer.convert_errno(e.errno) return SFTP_OK def readlink(self, path): path = self._realpath(path) try: symlink = os.readlink(path) except OSError as e: return SFTPServer.convert_errno(e.errno) # if it's absolute, remove the root if os.path.isabs(symlink): if symlink[:len(self.ROOT)] == self.ROOT: symlink = symlink[len(self.ROOT):] if (len(symlink) == 0) or (symlink[0] != '/'): symlink = '/' + symlink else: symlink = '<error>' return symlink