Support for Borg repository initialization via borgmatic --init command-line flag (#110).

This commit is contained in:
Dan Helfman 2018-12-10 22:20:57 -08:00
parent 2045edc11b
commit cc9dbb1def
13 changed files with 440 additions and 217 deletions

6
NEWS
View file

@ -1,7 +1,9 @@
1.2.12.dev0 1.2.12
* #110: Support for Borg repository initialization via borgmatic --init command-line flag.
* #111: Update Borg create --filter values so a dry run lists files to back up. * #111: Update Borg create --filter values so a dry run lists files to back up.
* #113: Update README with link to a new/forked Docker image. * #113: Update README with link to a new/forked Docker image.
* Error when deprecated --excludes command-line option is used. * Prevent deprecated --excludes command-line option from being used.
* Refactor README a bit to flow better for first-time users.
1.2.11 1.2.11
* #108: Support for Borg create --progress via borgmatic command-line flag. * #108: Support for Borg create --progress via borgmatic command-line flag.

287
README.md
View file

@ -58,28 +58,9 @@ href="https://asciinema.org/a/203761" target="_blank">screencast</a>.
To get up and running, first [install To get up and running, first [install
Borg](https://borgbackup.readthedocs.io/en/latest/installation.html), at Borg](https://borgbackup.readthedocs.io/en/latest/installation.html), at
least version 1.1. Then, follow the [Borg Quick least version 1.1.
Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) to create
a repository on a local or remote host.
Note that if you plan to run borgmatic on a schedule with cron, and you Then, run the following command to download and install borgmatic:
encrypt your Borg repository with a passphrase instead of a key file, you'll
either need to set the borgmatic `encryption_passphrase` configuration
variable or set the `BORG_PASSPHRASE` environment variable. See the
[repository encryption
section](https://borgbackup.readthedocs.io/en/latest/quickstart.html#repository-encryption)
of the Quick Start for more info.
Alternatively, the passphrase can be specified programatically by setting
either the borgmatic `encryption_passcommand` configuration variable or the
`BORG_PASSCOMMAND` environment variable. See the [Borg Security
FAQ](http://borgbackup.readthedocs.io/en/stable/faq.html#how-can-i-specify-the-encryption-passphrase-programmatically)
for more info.
If the repository is on a remote host, make sure that your local root user has
key-based ssh access to the desired user account on the remote host.
To install borgmatic, run the following command to download and install it:
```bash ```bash
sudo pip3 install --upgrade borgmatic sudo pip3 install --upgrade borgmatic
@ -88,6 +69,7 @@ sudo pip3 install --upgrade borgmatic
Note that your pip binary may have a different name than "pip3". Make sure Note that your pip binary may have a different name than "pip3". Make sure
you're using Python 3, as borgmatic does not support Python 2. you're using Python 3, as borgmatic does not support Python 2.
### Other ways to install ### Other ways to install
* [A borgmatic Docker image](https://hub.docker.com/r/monachus/borgmatic/) based * [A borgmatic Docker image](https://hub.docker.com/r/monachus/borgmatic/) based
@ -101,6 +83,7 @@ you're using Python 3, as borgmatic does not support Python 2.
* [A borgmatic package for OpenBSD](http://ports.su/sysutils/borgmatic). * [A borgmatic package for OpenBSD](http://ports.su/sysutils/borgmatic).
<br><br> <br><br>
## Configuration ## Configuration
After you install borgmatic, generate a sample configuration file: After you install borgmatic, generate a sample configuration file:
@ -124,6 +107,161 @@ borgmatic has added new options since you originally created your
configuration file. configuration file.
### Encryption
Note that if you plan to run borgmatic on a schedule with cron, and you
encrypt your Borg repository with a passphrase instead of a key file, you'll
either need to set the borgmatic `encryption_passphrase` configuration
variable or set the `BORG_PASSPHRASE` environment variable. See the
[repository encryption
section](https://borgbackup.readthedocs.io/en/latest/quickstart.html#repository-encryption)
of the Quick Start for more info.
Alternatively, the passphrase can be specified programatically by setting
either the borgmatic `encryption_passcommand` configuration variable or the
`BORG_PASSCOMMAND` environment variable. See the [Borg Security
FAQ](http://borgbackup.readthedocs.io/en/stable/faq.html#how-can-i-specify-the-encryption-passphrase-programmatically)
for more info.
## Usage
### Initialization
Before you can create backups with borgmatic, you first need to initialize a
Borg repository so you have a destination for your backup archives. (But skip
this step if you already have a Borg repository.) To create a repository, run
a command like the following:
```bash
borgmatic --init --encryption repokey
```
This uses the borgmatic configuration file you created above to determine
which local or remote repository to create, and encrypts it with the
encryption passphrase specified there if one is provided. Read about [Borg
encryption
modes](https://borgbackup.readthedocs.io/en/latest/usage/init.html#encryption-modes)
for the menu of available encryption modes.
Also, optionally check out the [Borg Quick
Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) for more
background about repository initialization.
If the repository is on a remote host, make sure that your local user has
key-based SSH access to the desired user account on the remote host.
### Backups
You can run borgmatic and start a backup simply by invoking it without
arguments:
```bash
borgmatic
```
This will also prune any old backups as per the configured retention policy,
and check backups for consistency problems due to things like file damage.
If you'd like to see the available command-line arguments, view the help:
```bash
borgmatic --help
```
Note that borgmatic prunes archives *before* creating an archive, so as to
free up space for archiving. This means that when a borgmatic run finishes,
there may still be prune-able archives. Not to worry, as they will get cleaned
up at the start of the next run.
### Verbosity
By default, the backup will proceed silently except in the case of errors. But
if you'd like to to get additional information about the progress of the
backup as it proceeds, use the verbosity option:
```bash
borgmatic --verbosity 1
```
Or, for even more progress spew:
```bash
borgmatic --verbosity 2
```
### À la carte
If you want to run borgmatic with only pruning, creating, or checking enabled,
the following optional flags are available:
```bash
borgmatic --prune
borgmatic --create
borgmatic --check
```
You can run with only one of these flags provided, or you can mix and match
any number of them. This supports use cases like running consistency checks
from a different cron job with a different frequency, or running pruning with
a different verbosity level.
Additionally, borgmatic provides convenient flags for Borg's
[list](https://borgbackup.readthedocs.io/en/stable/usage/list.html) and
[info](https://borgbackup.readthedocs.io/en/stable/usage/info.html)
functionality:
```bash
borgmatic --list
borgmatic --info
```
You can include an optional `--json` flag with `--create`, `--list`, or
`--info` to get the output formatted as JSON.
## Autopilot
If you want to run borgmatic automatically, say once a day, the you can
configure a job runner to invoke it periodically.
### cron
If you're using cron, download the [sample cron
file](https://projects.torsion.org/witten/borgmatic/src/master/sample/cron/borgmatic).
Then, from the directory where you downloaded it:
```bash
sudo mv borgmatic /etc/cron.d/borgmatic
sudo chmod +x /etc/cron.d/borgmatic
```
You can modify the cron file if you'd like to run borgmatic more or less frequently.
### systemd
If you're using systemd instead of cron to run jobs, download the [sample
systemd service
file](https://projects.torsion.org/witten/borgmatic/src/master/sample/systemd/borgmatic.service)
and the [sample systemd timer
file](https://projects.torsion.org/witten/borgmatic/src/master/sample/systemd/borgmatic.timer).
Then, from the directory where you downloaded them:
```bash
sudo mv borgmatic.service borgmatic.timer /etc/systemd/system/
sudo systemctl enable borgmatic.timer
sudo systemctl start borgmatic.timer
```
Feel free to modify the timer file based on how frequently you'd like
borgmatic to run.
## Advanced configuration
### Multiple configuration files ### Multiple configuration files
A more advanced usage is to create multiple separate configuration files and A more advanced usage is to create multiple separate configuration files and
@ -247,113 +385,6 @@ That's it! borgmatic will continue using your /etc/borgmatic configuration
files. files.
## Usage
You can run borgmatic and start a backup simply by invoking it without
arguments:
```bash
borgmatic
```
This will also prune any old backups as per the configured retention policy,
and check backups for consistency problems due to things like file damage.
If you'd like to see the available command-line arguments, view the help:
```bash
borgmatic --help
```
Note that borgmatic prunes archives *before* creating an archive, so as to
free up space for archiving. This means that when a borgmatic run finishes,
there may still be prune-able archives. Not to worry, as they will get cleaned
up at the start of the next run.
### Verbosity
By default, the backup will proceed silently except in the case of errors. But
if you'd like to to get additional information about the progress of the
backup as it proceeds, use the verbosity option:
```bash
borgmatic --verbosity 1
```
Or, for even more progress spew:
```bash
borgmatic --verbosity 2
```
### À la carte
If you want to run borgmatic with only pruning, creating, or checking enabled,
the following optional flags are available:
```bash
borgmatic --prune
borgmatic --create
borgmatic --check
```
You can run with only one of these flags provided, or you can mix and match
any number of them. This supports use cases like running consistency checks
from a different cron job with a different frequency, or running pruning with
a different verbosity level.
Additionally, borgmatic provides convenient flags for Borg's
[list](https://borgbackup.readthedocs.io/en/stable/usage/list.html) and
[info](https://borgbackup.readthedocs.io/en/stable/usage/info.html)
functionality:
```bash
borgmatic --list
borgmatic --info
```
You can include an optional `--json` flag with `--create`, `--list`, or
`--info` to get the output formatted as JSON.
## Autopilot
If you want to run borgmatic automatically, say once a day, the you can
configure a job runner to invoke it periodically.
### cron
If you're using cron, download the [sample cron
file](https://projects.torsion.org/witten/borgmatic/src/master/sample/cron/borgmatic).
Then, from the directory where you downloaded it:
```bash
sudo mv borgmatic /etc/cron.d/borgmatic
sudo chmod +x /etc/cron.d/borgmatic
```
You can modify the cron file if you'd like to run borgmatic more or less frequently.
### systemd
If you're using systemd instead of cron to run jobs, download the [sample
systemd service
file](https://projects.torsion.org/witten/borgmatic/src/master/sample/systemd/borgmatic.service)
and the [sample systemd timer
file](https://projects.torsion.org/witten/borgmatic/src/master/sample/systemd/borgmatic.timer).
Then, from the directory where you downloaded them:
```bash
sudo mv borgmatic.service borgmatic.timer /etc/systemd/system/
sudo systemctl enable borgmatic.timer
sudo systemctl start borgmatic.timer
```
Feel free to modify the timer file based on how frequently you'd like
borgmatic to run.
## Support and contributing ## Support and contributing
### Issues ### Issues

View file

@ -9,20 +9,6 @@ import tempfile
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def initialize_environment(storage_config):
passcommand = storage_config.get('encryption_passcommand')
if passcommand:
os.environ['BORG_PASSCOMMAND'] = passcommand
passphrase = storage_config.get('encryption_passphrase')
if passphrase:
os.environ['BORG_PASSPHRASE'] = passphrase
ssh_command = storage_config.get('ssh_command')
if ssh_command:
os.environ['BORG_RSH'] = ssh_command
def _expand_directory(directory): def _expand_directory(directory):
''' '''
Given a directory path, expand any tilde (representing a user's home directory) and any globs Given a directory path, expand any tilde (representing a user's home directory) and any globs

View file

@ -0,0 +1,15 @@
import os
def initialize(storage_config):
passcommand = storage_config.get('encryption_passcommand')
if passcommand:
os.environ['BORG_PASSCOMMAND'] = passcommand
passphrase = storage_config.get('encryption_passphrase')
if passphrase:
os.environ['BORG_PASSPHRASE'] = passphrase
ssh_command = storage_config.get('ssh_command')
if ssh_command:
os.environ['BORG_RSH'] = ssh_command

31
borgmatic/borg/init.py Normal file
View file

@ -0,0 +1,31 @@
import logging
import subprocess
logger = logging.getLogger(__name__)
def initialize_repository(
repository,
encryption_mode,
append_only=None,
storage_quota=None,
local_path='borg',
remote_path=None,
):
'''
Given a local or remote repository path, a Borg encryption mode, whether the repository should
be append-only, and the storage quota to use, initialize the repository.
'''
full_command = (
(local_path, 'init', repository)
+ (('--encryption', encryption_mode) if encryption_mode else ())
+ (('--append-only',) if append_only else ())
+ (('--storage-quota', storage_quota) if storage_quota else ())
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
+ (('--debug',) if logger.isEnabledFor(logging.DEBUG) else ())
+ (('--remote-path', remote_path) if remote_path else ())
)
logger.debug(' '.join(full_command))
subprocess.check_call(full_command)

View file

@ -8,9 +8,11 @@ import sys
from borgmatic.borg import ( from borgmatic.borg import (
check as borg_check, check as borg_check,
create as borg_create, create as borg_create,
environment as borg_environment,
prune as borg_prune, prune as borg_prune,
list as borg_list, list as borg_list,
info as borg_info, info as borg_info,
init as borg_init,
) )
from borgmatic.commands import hook from borgmatic.commands import hook
from borgmatic.config import checks, collect, convert, validate from borgmatic.config import checks, collect, convert, validate
@ -53,6 +55,26 @@ def parse_arguments(*arguments):
dest='excludes_filename', dest='excludes_filename',
help='Deprecated in favor of exclude_patterns within configuration', help='Deprecated in favor of exclude_patterns within configuration',
) )
parser.add_argument(
'-I', '--init', dest='init', action='store_true', help='Initialize an empty Borg repository'
)
parser.add_argument(
'-e',
'--encryption',
dest='encryption_mode',
help='Borg repository encryption mode (for use with --init)',
)
parser.add_argument(
'--append-only',
dest='append_only',
action='store_true',
help='Create an append-only repository (for use with --init)',
)
parser.add_argument(
'--storage-quota',
dest='storage_quota',
help='Create a repository with a fixed storage quota (for use with --init)',
)
parser.add_argument( parser.add_argument(
'-p', '-p',
'--prune', '--prune',
@ -111,7 +133,21 @@ def parse_arguments(*arguments):
args = parser.parse_args(arguments) args = parser.parse_args(arguments)
if args.excludes_filename: if args.excludes_filename:
raise ValueError('The --excludes option has been replaced with exclude_patterns in configuration') raise ValueError(
'The --excludes option has been replaced with exclude_patterns in configuration'
)
if (args.encryption_mode or args.append_only or args.storage_quota) and not args.init:
raise ValueError(
'The --encryption, --append-only, and --storage-quota options can only be used with the --init option'
)
if args.init and (args.prune or args.create or args.dry_run):
raise ValueError(
'The --init option cannot be used with the --prune, --create, or --dry-run options'
)
if args.init and not args.encryption_mode:
raise ValueError('The --encryption option is required with the --init option')
if args.progress and not args.create: if args.progress and not args.create:
raise ValueError('The --progress option can only be used with the --create option') raise ValueError('The --progress option can only be used with the --create option')
@ -128,7 +164,7 @@ def parse_arguments(*arguments):
# If any of the action flags are explicitly requested, leave them as-is. Otherwise, assume # If any of the action flags are explicitly requested, leave them as-is. Otherwise, assume
# defaults: Mutate the given arguments to enable the default actions. # defaults: Mutate the given arguments to enable the default actions.
if args.prune or args.create or args.check or args.list or args.info: if args.init or args.prune or args.create or args.check or args.list or args.info:
return args return args
args.prune = True args.prune = True
@ -152,7 +188,7 @@ def run_configuration(config_filename, args): # pragma: no cover
try: try:
local_path = location.get('local_path', 'borg') local_path = location.get('local_path', 'borg')
remote_path = location.get('remote_path') remote_path = location.get('remote_path')
borg_create.initialize_environment(storage) borg_environment.initialize(storage)
if args.create: if args.create:
hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup') hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup')
@ -206,6 +242,16 @@ def _run_commands_on_repository(
): # pragma: no cover ): # pragma: no cover
repository = os.path.expanduser(unexpanded_repository) repository = os.path.expanduser(unexpanded_repository)
dry_run_label = ' (dry run; not making any changes)' if args.dry_run else '' dry_run_label = ' (dry run; not making any changes)' if args.dry_run else ''
if args.init:
logger.info('{}: Initializing repository'.format(repository))
borg_init.initialize_repository(
repository,
args.encryption_mode,
args.append_only,
args.storage_quota,
local_path=local_path,
remote_path=remote_path,
)
if args.prune: if args.prune:
logger.info('{}: Pruning archives{}'.format(repository, dry_run_label)) logger.info('{}: Pruning archives{}'.format(repository, dry_run_label))
borg_prune.prune_archives( borg_prune.prune_archives(

View file

@ -1,7 +1,7 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
VERSION = '1.2.12.dev0' VERSION = '1.2.12'
setup( setup(

View file

@ -9,7 +9,8 @@ import tempfile
def generate_configuration(config_path, repository_path): def generate_configuration(config_path, repository_path):
''' '''
Generate borgmatic configuration into a file at the config path, and update the defaults so as Generate borgmatic configuration into a file at the config path, and update the defaults so as
to work for testing (including injecting the given repository path). to work for testing (including injecting the given repository path and tacking on an encryption
passphrase).
''' '''
subprocess.check_call( subprocess.check_call(
'generate-borgmatic-config --destination {}'.format(config_path).split(' ') 'generate-borgmatic-config --destination {}'.format(config_path).split(' ')
@ -21,6 +22,7 @@ def generate_configuration(config_path, repository_path):
.replace('- /home', '- {}'.format(config_path)) .replace('- /home', '- {}'.format(config_path))
.replace('- /etc', '') .replace('- /etc', '')
.replace('- /var/log/syslog*', '') .replace('- /var/log/syslog*', '')
+ 'storage:\n encryption_passphrase: "test"'
) )
config_file = open(config_path, 'w') config_file = open(config_path, 'w')
config_file.write(config) config_file.write(config)
@ -33,14 +35,13 @@ def test_borgmatic_command():
repository_path = os.path.join(temporary_directory, 'test.borg') repository_path = os.path.join(temporary_directory, 'test.borg')
try: try:
subprocess.check_call(
'borg init --encryption repokey {}'.format(repository_path).split(' '),
env={'BORG_PASSPHRASE': '', **os.environ},
)
config_path = os.path.join(temporary_directory, 'test.yaml') config_path = os.path.join(temporary_directory, 'test.yaml')
generate_configuration(config_path, repository_path) generate_configuration(config_path, repository_path)
subprocess.check_call(
'borgmatic -v 2 --config {} --init --encryption repokey'.format(config_path).split(' ')
)
# Run borgmatic to generate a backup archive, and then list it to make sure it exists. # Run borgmatic to generate a backup archive, and then list it to make sure it exists.
subprocess.check_call('borgmatic --config {}'.format(config_path).split(' ')) subprocess.check_call('borgmatic --config {}'.format(config_path).split(' '))
output = subprocess.check_output( output = subprocess.check_output(

View file

@ -16,13 +16,6 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
assert parser.json is False assert parser.json is False
def test_parse_arguments_disallows_deprecated_excludes_option():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--excludes', 'myexcludes')
def test_parse_arguments_with_multiple_config_paths_parses_as_list(): def test_parse_arguments_with_multiple_config_paths_parses_as_list():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
@ -32,7 +25,7 @@ def test_parse_arguments_with_multiple_config_paths_parses_as_list():
assert parser.verbosity is 0 assert parser.verbosity is 0
def test_parse_arguments_with_verbosity_flag_overrides_default(): def test_parse_arguments_with_verbosity_overrides_default():
config_paths = ['default'] config_paths = ['default']
flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths) flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
@ -43,7 +36,7 @@ def test_parse_arguments_with_verbosity_flag_overrides_default():
assert parser.verbosity == 1 assert parser.verbosity == 1
def test_parse_arguments_with_json_flag_overrides_default(): def test_parse_arguments_with_json_overrides_default():
parser = module.parse_arguments('--list', '--json') parser = module.parse_arguments('--list', '--json')
assert parser.json is True assert parser.json is True
@ -85,25 +78,81 @@ def test_parse_arguments_with_invalid_arguments_exits():
module.parse_arguments('--posix-me-harder') module.parse_arguments('--posix-me-harder')
def test_parse_arguments_with_progress_and_create_flags_does_not_raise(): def test_parse_arguments_disallows_deprecated_excludes_option():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--excludes', 'myexcludes')
def test_parse_arguments_disallows_encryption_mode_without_init():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--encryption', 'repokey')
def test_parse_arguments_requires_encryption_mode_with_init():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--init')
def test_parse_arguments_disallows_append_only_without_init():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--append-only')
def test_parse_arguments_disallows_storage_quota_without_init():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--storage-quota', '5G')
def test_parse_arguments_disallows_init_and_prune():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--init', '--prune')
def test_parse_arguments_disallows_init_and_create():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--init', '--create')
def test_parse_arguments_disallows_init_and_dry_run():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--init', '--dry-run')
def test_parse_arguments_allows_progress_and_create():
module.parse_arguments('--progress', '--create', '--list') module.parse_arguments('--progress', '--create', '--list')
def test_parse_arguments_with_progress_flag_but_no_create_flag_raises_value_error(): def test_parse_arguments_disallows_progress_without_create():
with pytest.raises(ValueError): with pytest.raises(ValueError):
module.parse_arguments('--progress', '--list') module.parse_arguments('--progress', '--list')
def test_parse_arguments_with_json_flag_with_list_or_info_flag_does_not_raise_any_error(): def test_parse_arguments_allows_json_with_list_or_info():
module.parse_arguments('--list', '--json') module.parse_arguments('--list', '--json')
module.parse_arguments('--info', '--json') module.parse_arguments('--info', '--json')
def test_parse_arguments_with_json_flag_but_no_list_or_info_flag_raises_value_error(): def test_parse_arguments_disallows_json_without_list_or_info():
with pytest.raises(ValueError): with pytest.raises(ValueError):
module.parse_arguments('--json') module.parse_arguments('--json')
def test_parse_arguments_with_json_flag_and_both_list_and_info_flag_raises_value_error(): def test_parse_arguments_disallows_json_with_both_list_and_info():
with pytest.raises(ValueError): with pytest.raises(ValueError):
module.parse_arguments('--list', '--info', '--json') module.parse_arguments('--list', '--info', '--json')

View file

@ -1,5 +1,4 @@
import logging import logging
import os
from flexmock import flexmock from flexmock import flexmock
@ -7,52 +6,6 @@ from borgmatic.borg import create as module
from ..test_verbosity import insert_logging_mock from ..test_verbosity import insert_logging_mock
def test_initialize_environment_with_passcommand_should_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize_environment({'encryption_passcommand': 'command'})
assert os.environ.get('BORG_PASSCOMMAND') == 'command'
finally:
os.environ = orig_environ
def test_initialize_environment_with_passphrase_should_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize_environment({'encryption_passphrase': 'pass'})
assert os.environ.get('BORG_PASSPHRASE') == 'pass'
finally:
os.environ = orig_environ
def test_initialize_environment_with_ssh_command_should_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize_environment({'ssh_command': 'ssh -C'})
assert os.environ.get('BORG_RSH') == 'ssh -C'
finally:
os.environ = orig_environ
def test_initialize_environment_without_configuration_should_not_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize_environment({})
assert os.environ.get('BORG_PASSCOMMAND') is None
assert os.environ.get('BORG_PASSPHRASE') is None
assert os.environ.get('BORG_RSH') is None
finally:
os.environ = orig_environ
def test_expand_directory_with_basic_path_passes_it_through(): def test_expand_directory_with_basic_path_passes_it_through():
flexmock(module.os.path).should_receive('expanduser').and_return('foo') flexmock(module.os.path).should_receive('expanduser').and_return('foo')
flexmock(module.glob).should_receive('glob').and_return([]) flexmock(module.glob).should_receive('glob').and_return([])

View file

@ -0,0 +1,49 @@
import os
from borgmatic.borg import environment as module
def test_initialize_with_passcommand_should_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize({'encryption_passcommand': 'command'})
assert os.environ.get('BORG_PASSCOMMAND') == 'command'
finally:
os.environ = orig_environ
def test_initialize_with_passphrase_should_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize({'encryption_passphrase': 'pass'})
assert os.environ.get('BORG_PASSPHRASE') == 'pass'
finally:
os.environ = orig_environ
def test_initialize_with_ssh_command_should_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize({'ssh_command': 'ssh -C'})
assert os.environ.get('BORG_RSH') == 'ssh -C'
finally:
os.environ = orig_environ
def test_initialize_without_configuration_should_not_set_environment():
orig_environ = os.environ
try:
os.environ = {}
module.initialize({})
assert os.environ.get('BORG_PASSCOMMAND') is None
assert os.environ.get('BORG_PASSPHRASE') is None
assert os.environ.get('BORG_RSH') is None
finally:
os.environ = orig_environ

View file

@ -0,0 +1,58 @@
import logging
from flexmock import flexmock
from borgmatic.borg import init as module
from ..test_verbosity import insert_logging_mock
def insert_subprocess_mock(check_call_command, **kwargs):
subprocess = flexmock(module.subprocess)
subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once()
INIT_COMMAND = ('borg', 'init', 'repo', '--encryption', 'repokey')
def test_initialize_repository_calls_borg_with_parameters():
insert_subprocess_mock(INIT_COMMAND)
module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_with_append_only_calls_borg_with_append_only_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--append-only',))
module.initialize_repository(repository='repo', encryption_mode='repokey', append_only=True)
def test_initialize_repository_with_storage_quota_calls_borg_with_storage_quota_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--storage-quota', '5G'))
module.initialize_repository(repository='repo', encryption_mode='repokey', storage_quota='5G')
def test_initialize_repository_with_log_info_calls_borg_with_info_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--info',))
insert_logging_mock(logging.INFO)
module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_with_log_debug_calls_borg_with_debug_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--debug',))
insert_logging_mock(logging.DEBUG)
module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_with_local_path_calls_borg_via_local_path():
insert_subprocess_mock(('borg1',) + INIT_COMMAND[1:])
module.initialize_repository(repository='repo', encryption_mode='repokey', local_path='borg1')
def test_initialize_repository_with_remote_path_calls_borg_with_remote_path_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--remote-path', 'borg1'))
module.initialize_repository(repository='repo', encryption_mode='repokey', remote_path='borg1')

View file

@ -6,9 +6,11 @@ from borgmatic import verbosity as module
def insert_logging_mock(log_level): def insert_logging_mock(log_level):
""" Mocks the isEnabledFor from python logging. """ '''
Mock the isEnabledFor from Python logging.
'''
logging = flexmock(module.logging.Logger) logging = flexmock(module.logging.Logger)
logging.should_receive('isEnabledFor').replace_with(lambda lvl: lvl >= log_level) logging.should_receive('isEnabledFor').replace_with(lambda level: level >= log_level)
logging.should_receive('getEffectiveLevel').replace_with(lambda: log_level) logging.should_receive('getEffectiveLevel').replace_with(lambda: log_level)