Merge pull request #4299 from ThomasWaldmann/add-rsh-arg-1.1

Add --rsh command line option to complement BORG_RSH env var. Fixes #1701
This commit is contained in:
TW 2019-02-01 14:22:02 +01:00 committed by GitHub
commit 8a523a15a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 13 deletions

View file

@ -434,6 +434,7 @@ When set, use the given filename as \fI\%INI\fP\-style logging configuration.
.B BORG_RSH
When set, use this command instead of \fBssh\fP\&. This can be used to specify ssh options, such as
a custom identity file \fBssh \-i /path/to/private/key\fP\&. See \fBman ssh\fP for other options.
Using the \fB\-\-rsh CMD\fP commandline option overrides the environment variable.
.TP
.B BORG_REMOTE_PATH
When set, use the given path as borg executable on the remote (defaults to "borg" if unset).

View file

@ -204,7 +204,8 @@ General:
When set, use the given filename as INI_-style logging configuration.
BORG_RSH
When set, use this command instead of ``ssh``. This can be used to specify ssh options, such as
a custom identity file ``ssh -i /path/to/private/key``. See ``man ssh`` for other options.
a custom identity file ``ssh -i /path/to/private/key``. See ``man ssh`` for other options. Using
the ``--rsh CMD`` commandline option overrides the environment variable.
BORG_REMOTE_PATH
When set, use the given path as borg executable on the remote (defaults to "borg" if unset).
Using ``--remote-path PATH`` commandline option overrides the environment variable.

View file

@ -2664,6 +2664,8 @@ class Archiver:
add_common_option('--debug-profile', metavar='FILE', dest='debug_profile', default=None,
help='Write execution profile in Borg format into FILE. For local use a Python-'
'compatible file can be generated by suffixing FILE with ".pyprof".')
add_common_option('--rsh', metavar='RSH', dest='rsh',
help="Use this command to connect to the 'borg serve' process (default: 'ssh')")
def define_exclude_and_patterns(add_option, *, tag_files=False, strip_components=False):
add_option('-e', '--exclude', metavar='PATTERN', dest='patterns',

View file

@ -546,6 +546,7 @@ class RemoteRepository:
self.unpacker = get_limited_unpacker('client')
self.server_version = parse_version('1.0.8') # fallback version if server is too old to send version information
self.p = None
self._args = args
testing = location.host == '__testsuite__'
# when testing, we invoke and talk to a borg process directly (no ssh).
# when not testing, we invoke the system-installed ssh binary to talk to a remote borg.
@ -691,7 +692,8 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
def ssh_cmd(self, location):
"""return a ssh command line that can be prefixed to a borg command line"""
args = shlex.split(os.environ.get('BORG_RSH', 'ssh'))
rsh = self._args.rsh or os.environ.get('BORG_RSH', 'ssh')
args = shlex.split(rsh)
if location.port:
args += ['-p', str(location.port)]
if location.user:

View file

@ -798,6 +798,19 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
return RemoteRepository(Location('__testsuite__:' + os.path.join(self.tmppath, 'repository')),
exclusive=True, create=create)
def _get_mock_args(self):
class MockArgs:
remote_path = 'borg'
umask = 0o077
debug_topics = []
rsh = None
def __contains__(self, item):
# To behave like argparse.Namespace
return hasattr(self, item)
return MockArgs()
def test_invalid_rpc(self):
self.assert_raises(InvalidRPCMethod, lambda: self.repository.call('__init__', {}))
@ -856,6 +869,8 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
assert len(e.exception_full) > 0
def test_ssh_cmd(self):
args = self._get_mock_args()
self.repository._args = args
assert self.repository.ssh_cmd(Location('example.com:foo')) == ['ssh', 'example.com']
assert self.repository.ssh_cmd(Location('ssh://example.com/foo')) == ['ssh', 'example.com']
assert self.repository.ssh_cmd(Location('ssh://user@example.com/foo')) == ['ssh', 'user@example.com']
@ -864,17 +879,8 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
assert self.repository.ssh_cmd(Location('example.com:foo')) == ['ssh', '--foo', 'example.com']
def test_borg_cmd(self):
class MockArgs:
remote_path = 'borg'
umask = 0o077
debug_topics = []
def __contains__(self, item):
# To behave like argparse.Namespace
return hasattr(self, item)
assert self.repository.borg_cmd(None, testing=True) == [sys.executable, '-m', 'borg.archiver', 'serve']
args = MockArgs()
args = self._get_mock_args()
# XXX without next line we get spurious test fails when using pytest-xdist, root cause unknown:
logging.getLogger().setLevel(logging.INFO)
# note: test logger is on info log level, so --info gets added automagically
@ -884,12 +890,15 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
args.debug_topics = ['something_client_side', 'repository_compaction']
assert self.repository.borg_cmd(args, testing=False) == ['borg-0.28.2', 'serve', '--umask=077', '--info',
'--debug-topic=borg.debug.repository_compaction']
args = MockArgs()
args = self._get_mock_args()
args.storage_quota = 0
assert self.repository.borg_cmd(args, testing=False) == ['borg', 'serve', '--umask=077', '--info']
args.storage_quota = 314159265
assert self.repository.borg_cmd(args, testing=False) == ['borg', 'serve', '--umask=077', '--info',
'--storage-quota=314159265']
args.rsh = 'ssh -i foo'
self.repository._args = args
assert self.repository.ssh_cmd(Location('example.com:foo')) == ['ssh', '-i', 'foo', 'example.com']
class RemoteLegacyFree(RepositoryTestCaseBase):