diff --git a/NEWS b/NEWS
index 54cf6c6..50ed98f 100644
--- a/NEWS
+++ b/NEWS
@@ -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.
* #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
* #108: Support for Borg create --progress via borgmatic command-line flag.
diff --git a/README.md b/README.md
index 90c4d72..12961bb 100644
--- a/README.md
+++ b/README.md
@@ -58,28 +58,9 @@ href="https://asciinema.org/a/203761" target="_blank">screencast.
To get up and running, first [install
Borg](https://borgbackup.readthedocs.io/en/latest/installation.html), at
-least version 1.1. Then, follow the [Borg Quick
-Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) to create
-a repository on a local or remote host.
+least version 1.1.
-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.
-
-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:
+Then, run the following command to download and install borgmatic:
```bash
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
you're using Python 3, as borgmatic does not support Python 2.
+
### Other ways to install
* [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).
+
## Configuration
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.
+### 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
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.
-## 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
### Issues
diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py
index 2b0af39..a446380 100644
--- a/borgmatic/borg/create.py
+++ b/borgmatic/borg/create.py
@@ -9,20 +9,6 @@ import tempfile
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):
'''
Given a directory path, expand any tilde (representing a user's home directory) and any globs
diff --git a/borgmatic/borg/environment.py b/borgmatic/borg/environment.py
new file mode 100644
index 0000000..0f717e7
--- /dev/null
+++ b/borgmatic/borg/environment.py
@@ -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
diff --git a/borgmatic/borg/init.py b/borgmatic/borg/init.py
new file mode 100644
index 0000000..b9bdf9a
--- /dev/null
+++ b/borgmatic/borg/init.py
@@ -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)
diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py
index 78eef23..6be3933 100644
--- a/borgmatic/commands/borgmatic.py
+++ b/borgmatic/commands/borgmatic.py
@@ -8,9 +8,11 @@ import sys
from borgmatic.borg import (
check as borg_check,
create as borg_create,
+ environment as borg_environment,
prune as borg_prune,
list as borg_list,
info as borg_info,
+ init as borg_init,
)
from borgmatic.commands import hook
from borgmatic.config import checks, collect, convert, validate
@@ -53,6 +55,26 @@ def parse_arguments(*arguments):
dest='excludes_filename',
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(
'-p',
'--prune',
@@ -111,7 +133,21 @@ def parse_arguments(*arguments):
args = parser.parse_args(arguments)
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:
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
# 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
args.prune = True
@@ -152,7 +188,7 @@ def run_configuration(config_filename, args): # pragma: no cover
try:
local_path = location.get('local_path', 'borg')
remote_path = location.get('remote_path')
- borg_create.initialize_environment(storage)
+ borg_environment.initialize(storage)
if args.create:
hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup')
@@ -206,6 +242,16 @@ def _run_commands_on_repository(
): # pragma: no cover
repository = os.path.expanduser(unexpanded_repository)
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:
logger.info('{}: Pruning archives{}'.format(repository, dry_run_label))
borg_prune.prune_archives(
diff --git a/setup.py b/setup.py
index 5f9650d..996067a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
-VERSION = '1.2.12.dev0'
+VERSION = '1.2.12'
setup(
diff --git a/tests/end-to-end/test_borgmatic.py b/tests/end-to-end/test_borgmatic.py
index a0314aa..76802d3 100644
--- a/tests/end-to-end/test_borgmatic.py
+++ b/tests/end-to-end/test_borgmatic.py
@@ -9,7 +9,8 @@ import tempfile
def generate_configuration(config_path, repository_path):
'''
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(
'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('- /etc', '')
.replace('- /var/log/syslog*', '')
+ + 'storage:\n encryption_passphrase: "test"'
)
config_file = open(config_path, 'w')
config_file.write(config)
@@ -33,14 +35,13 @@ def test_borgmatic_command():
repository_path = os.path.join(temporary_directory, 'test.borg')
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')
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.
subprocess.check_call('borgmatic --config {}'.format(config_path).split(' '))
output = subprocess.check_output(
diff --git a/tests/integration/commands/test_borgmatic.py b/tests/integration/commands/test_borgmatic.py
index 66ffc5e..cbbd4a0 100644
--- a/tests/integration/commands/test_borgmatic.py
+++ b/tests/integration/commands/test_borgmatic.py
@@ -16,13 +16,6 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
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():
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
-def test_parse_arguments_with_verbosity_flag_overrides_default():
+def test_parse_arguments_with_verbosity_overrides_default():
config_paths = ['default']
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
-def test_parse_arguments_with_json_flag_overrides_default():
+def test_parse_arguments_with_json_overrides_default():
parser = module.parse_arguments('--list', '--json')
assert parser.json is True
@@ -85,25 +78,81 @@ def test_parse_arguments_with_invalid_arguments_exits():
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')
-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):
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('--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):
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):
module.parse_arguments('--list', '--info', '--json')
diff --git a/tests/unit/borg/test_create.py b/tests/unit/borg/test_create.py
index f8840e5..14ccbf7 100644
--- a/tests/unit/borg/test_create.py
+++ b/tests/unit/borg/test_create.py
@@ -1,5 +1,4 @@
import logging
-import os
from flexmock import flexmock
@@ -7,52 +6,6 @@ from borgmatic.borg import create as module
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():
flexmock(module.os.path).should_receive('expanduser').and_return('foo')
flexmock(module.glob).should_receive('glob').and_return([])
diff --git a/tests/unit/borg/test_environment.py b/tests/unit/borg/test_environment.py
new file mode 100644
index 0000000..67653b3
--- /dev/null
+++ b/tests/unit/borg/test_environment.py
@@ -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
diff --git a/tests/unit/borg/test_init.py b/tests/unit/borg/test_init.py
new file mode 100644
index 0000000..67ed8a8
--- /dev/null
+++ b/tests/unit/borg/test_init.py
@@ -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')
diff --git a/tests/unit/test_verbosity.py b/tests/unit/test_verbosity.py
index cb1f3af..9069caa 100644
--- a/tests/unit/test_verbosity.py
+++ b/tests/unit/test_verbosity.py
@@ -6,9 +6,11 @@ from borgmatic import verbosity as module
def insert_logging_mock(log_level):
- """ Mocks the isEnabledFor from python logging. """
+ '''
+ Mock the isEnabledFor from Python logging.
+ '''
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)