Compare commits

..
7 Commits
13 changed files with 89 additions and 37 deletions
+7 -1
View File
@@ -1,3 +1,9 @@
1.9.1
* #928: Fix the user runtime directory location on macOS (and possibly Cygwin).
* #930: Fix an error with the sample systemd service when no credentials are configured.
* #931: Fix an error when implicitly upgrading the check state directory from ~/.borgmatic to
~/.local/state/borgmatic across filesystems.
1.9.0
* #609: Fix the glob expansion of "source_directories" values to respect the "working_directory"
option.
@@ -20,6 +26,7 @@
cases.
* #902: Add loading of encrypted systemd credentials. See the documentation for more information:
https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/#using-systemd-service-credentials
* #911: Add a "key change-passphrase" action to change the passphrase protecting a repository key.
* #914: Fix a confusing apparent hang when when the repository location changes, and instead
show a helpful error message.
* #915: BREAKING: Rename repository actions like "rcreate" to more explicit names like
@@ -31,7 +38,6 @@
* #919: Clarify the command-line help for the "--config" flag.
* #919: Document a policy for versioning and breaking changes:
https://torsion.org/borgmatic/docs/how-to/upgrade/#versioning-and-breaking-changes
* #911: Add a "key change-passphrase" action to change the passphrase protecting a repository key.
* #921: BREAKING: Change soft failure command hooks to skip only the current repository rather than
all repositories in the configuration file.
* #922: Replace setup.py (Python packaging metadata) with the more modern pyproject.toml.
+4 -3
View File
@@ -6,6 +6,7 @@ import logging
import os
import pathlib
import random
import shutil
import borgmatic.borg.check
import borgmatic.borg.create
@@ -322,7 +323,7 @@ def upgrade_check_times(config, borg_repository_id):
f'Upgrading archives check times directory from {borgmatic_source_checks_path} to {borgmatic_state_checks_path}'
)
os.makedirs(borgmatic_state_path, mode=0o700, exist_ok=True)
os.rename(borgmatic_source_checks_path, borgmatic_state_checks_path)
shutil.move(borgmatic_source_checks_path, borgmatic_state_checks_path)
for check_type in ('archives', 'data'):
new_path = make_check_time_path(config, borg_repository_id, check_type, 'all')
@@ -335,12 +336,12 @@ def upgrade_check_times(config, borg_repository_id):
logger.debug(f'Upgrading archives check time file from {old_path} to {new_path}')
try:
os.rename(old_path, temporary_path)
shutil.move(old_path, temporary_path)
except FileNotFoundError:
pass
os.mkdir(old_path)
os.rename(temporary_path, new_path)
shutil.move(temporary_path, new_path)
def collect_spot_check_source_paths(
+3 -1
View File
@@ -91,7 +91,9 @@ def strip_path_prefix_from_extracted_dump_destination(
if not databases_directory.endswith('_databases'):
continue
os.rename(subdirectory_path, os.path.join(borgmatic_runtime_directory, databases_directory))
shutil.move(
subdirectory_path, os.path.join(borgmatic_runtime_directory, databases_directory)
)
break
+1 -1
View File
@@ -876,7 +876,7 @@ def make_parsers():
)
config_bootstrap_group.add_argument(
'--user-runtime-directory',
help='Path used for temporary runtime data like bootstrap metadata. Defaults to $XDG_RUNTIME_DIR or /var/run/$UID',
help='Path used for temporary runtime data like bootstrap metadata. Defaults to $XDG_RUNTIME_DIR or $TMPDIR or $TEMP or /var/run/$UID',
)
config_bootstrap_group.add_argument(
'--borgmatic-source-directory',
+6 -5
View File
@@ -30,7 +30,8 @@ def get_borgmatic_runtime_directory(config):
'''
Given a configuration dict, get the borgmatic runtime directory used for storing temporary
runtime data like streaming database dumps and bootstrap metadata. Defaults to
$XDG_RUNTIME_DIR/./borgmatic or /run/user/$UID/./borgmatic.
$XDG_RUNTIME_DIR/./borgmatic or $TMPDIR/./borgmatic or $TEMP/./borgmatic or
/run/user/$UID/./borgmatic.
The "/./" is taking advantage of a Borg feature such that the part of the path before the "/./"
does not get stored in the file path within an archive. That way, the path of the runtime
@@ -39,10 +40,10 @@ def get_borgmatic_runtime_directory(config):
return expand_user_in_path(
os.path.join(
config.get('user_runtime_directory')
or os.environ.get(
'XDG_RUNTIME_DIR',
f'/run/user/{os.getuid()}',
),
or os.environ.get('XDG_RUNTIME_DIR')
or os.environ.get('TMPDIR')
or os.environ.get('TEMP')
or f'/run/user/{os.getuid()}',
'.',
'borgmatic',
)
+1 -1
View File
@@ -218,7 +218,7 @@ properties:
Path for storing temporary runtime data like streaming database
dumps and bootstrap metadata. borgmatic automatically creates and
uses a "borgmatic" subdirectory here. Defaults to $XDG_RUNTIME_DIR
or /run/user/$UID.
or or $TMPDIR or $TEMP or /run/user/$UID.
example: /run/user/1001/borgmatic
user_state_directory:
type: string
+5
View File
@@ -76,6 +76,11 @@ borgmatic created temporary streaming database dumps within the `~/.borgmatic`
directory by default. At that time, the path was configurable by the
`borgmatic_source_directory` configuration option (now deprecated).
<span class="minilink minilink-addedin">New in version 1.9.1</span>In addition
to `XDG_RUNTIME_DIR`, borgmatic also uses the `TMPDIR` or `TEMP` environment
variable if set. `TMPDIR` is available on macOS, while `TEMP` is often
available on other platforms.
Also note that using a database hook implicitly enables the
`read_special` configuration option (even if it's disabled in your
configuration) to support this dump and restore streaming. See Limitations
+24 -6
View File
@@ -39,9 +39,15 @@ Save your password as an encrypted credential to `/etc/credstore.encrypted/borgm
# systemd-ask-password -n | systemd-creds encrypt - /etc/credstore.encrypted/borgmatic.pw
```
Then uncomment or use the following in your configuration file:
```yaml
encryption_passcommand: "cat ${CREDENTIALS_DIRECTORY}/borgmatic.pw"
```
Note that the name `borgmatic.pw` is hardcoded in the systemd service file.
If you use multiple different passwords, save them as encrypted credentials to `/etc/credstore.encrypted/borgmatic/`, e.g.,
To use multiple different passwords, save them as encrypted credentials to `/etc/credstore.encrypted/borgmatic/`, e.g.,
```
# mkdir /etc/credstore.encrypted/borgmatic
@@ -49,17 +55,29 @@ If you use multiple different passwords, save them as encrypted credentials to `
# systemd-ask-password -n | systemd-creds encrypt --name=borgmatic_pw2 - /etc/credstore.encrypted/borgmatic/pw2
...
```
Ensure that the file names, (e.g. `backupserver1`) match the corresponding part of
the `--name` option *after* the underscore (_). The `borgmatic` folder is hardcoded in the systemd service file.
the `--name` option *after* the underscore (_), and that the part *before*
the underscore matches the directory name (e.g. `borgmatic`).
Then uncomment or use one of the following in your configuration file. Adjust `borgmatic_backupserver1`
according to the name given to the credential.
Then, uncomment the appropriate line in the systemd service file:
```yaml
encryption_passcommand: "cat ${CREDENTIALS_DIRECTORY}/borgmatic.pw"
```
# systemctl edit borgmatic.service
...
# Load multiple encrypted credentials.
LoadCredentialEncrypted=borgmatic:/etc/credstore.encrypted/borgmatic/
```
Finally, use the following in your configuration file:
```
encryption_passcommand: "cat ${CREDENTIALS_DIRECTORY}/borgmatic_backupserver1"
```
Adjust `borgmatic_backupserver1` according to the name given to the credential
and the directory set in the service file.
### Environment variable interpolation
<span class="minilink minilink-addedin">New in version 1.6.4</span> borgmatic
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "borgmatic"
version = "1.9.0"
version = "1.9.1"
authors = [
{ name="Dan Helfman", email="witten@torsion.org" },
]
+4 -2
View File
@@ -10,10 +10,12 @@ Documentation=https://torsion.org/borgmatic/
[Service]
Type=oneshot
# Load encrypted credentials.
LoadCredentialEncrypted=borgmatic:/etc/credstore.encrypted/borgmatic/
# Load single encrypted credential.
LoadCredentialEncrypted=borgmatic.pw
# Load multiple encrypted credentials.
# LoadCredentialEncrypted=borgmatic:/etc/credstore.encrypted/borgmatic/
# Security settings for systemd running as root, optional but recommended to improve security. You
# can disable individual settings if they cause problems for your use case. For more details, see
# the systemd manual: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
+10 -10
View File
@@ -381,7 +381,7 @@ def test_upgrade_check_times_moves_checks_from_borgmatic_source_directory_to_sta
'/home/user/.local/state/borgmatic/checks'
).and_return(False)
flexmock(module.os).should_receive('makedirs')
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
'/home/user/.borgmatic/checks', '/home/user/.local/state/borgmatic/checks'
).once()
@@ -408,13 +408,13 @@ def test_upgrade_check_times_with_checks_already_in_borgmatic_state_directory_do
'/home/user/.local/state/borgmatic/checks'
).and_return(True)
flexmock(module.os).should_receive('makedirs').never()
flexmock(module.os).should_receive('rename').never()
flexmock(module.shutil).should_receive('move').never()
flexmock(module).should_receive('make_check_time_path').and_return(
'/home/user/.local/state/borgmatic/checks/1234/archives/all'
)
flexmock(module.os.path).should_receive('isfile').and_return(False)
flexmock(module.os).should_receive('rename').never()
flexmock(module.shutil).should_receive('move').never()
flexmock(module.os).should_receive('mkdir').never()
module.upgrade_check_times(flexmock(), flexmock())
@@ -448,11 +448,11 @@ def test_upgrade_check_times_renames_old_check_paths_to_all():
flexmock(module.os.path).should_receive('isfile').with_args(
f'{base_path}/data.temp'
).and_return(False)
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
f'{base_path}/archives', f'{base_path}/archives.temp'
).once()
flexmock(module.os).should_receive('mkdir').with_args(f'{base_path}/archives').once()
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
f'{base_path}/archives.temp', f'{base_path}/archives/all'
).once()
@@ -484,11 +484,11 @@ def test_upgrade_check_times_renames_data_check_paths_when_archives_paths_are_al
flexmock(module.os.path).should_receive('isfile').with_args(f'{base_path}/data').and_return(
True
)
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
f'{base_path}/data', f'{base_path}/data.temp'
).once()
flexmock(module.os).should_receive('mkdir').with_args(f'{base_path}/data').once()
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
f'{base_path}/data.temp', f'{base_path}/data/all'
).once()
@@ -508,7 +508,7 @@ def test_upgrade_check_times_skips_already_upgraded_check_paths():
'/home/user/.local/state/borgmatic/checks/1234/archives/all'
)
flexmock(module.os.path).should_receive('isfile').and_return(False)
flexmock(module.os).should_receive('rename').never()
flexmock(module.shutil).should_receive('move').never()
flexmock(module.os).should_receive('mkdir').never()
module.upgrade_check_times(flexmock(), flexmock())
@@ -542,11 +542,11 @@ def test_upgrade_check_times_renames_stale_temporary_check_path():
flexmock(module.os.path).should_receive('isfile').with_args(
f'{base_path}/data.temp'
).and_return(False)
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
f'{base_path}/archives', f'{base_path}/archives.temp'
).and_raise(FileNotFoundError)
flexmock(module.os).should_receive('mkdir').with_args(f'{base_path}/archives').once()
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
f'{base_path}/archives.temp', f'{base_path}/archives/all'
).once()
+2 -2
View File
@@ -75,10 +75,10 @@ def test_strip_path_prefix_from_extracted_dump_destination_renames_first_matchin
]
)
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
'/foo/bar/postgresql_databases', '/run/user/0/borgmatic/postgresql_databases'
).once()
flexmock(module.os).should_receive('rename').with_args(
flexmock(module.shutil).should_receive('move').with_args(
'/foo/bar/mariadb_databases', '/run/user/0/borgmatic/mariadb_databases'
).never()
+21 -4
View File
@@ -44,11 +44,28 @@ def test_get_borgmatic_runtime_directory_uses_config_option():
)
def test_get_borgmatic_runtime_directory_falls_back_to_environment_variable():
def test_get_borgmatic_runtime_directory_falls_back_to_linux_environment_variable():
flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path)
flexmock(module.os.environ).should_receive('get').with_args(
'XDG_RUNTIME_DIR', object
).and_return('/tmp')
flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(
'/tmp'
)
assert module.get_borgmatic_runtime_directory({}) == '/tmp/./borgmatic'
def test_get_borgmatic_runtime_directory_falls_back_to_macos_environment_variable():
flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path)
flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None)
flexmock(module.os.environ).should_receive('get').with_args('TMPDIR').and_return('/tmp')
assert module.get_borgmatic_runtime_directory({}) == '/tmp/./borgmatic'
def test_get_borgmatic_runtime_directory_falls_back_to_other_environment_variable():
flexmock(module).should_receive('expand_user_in_path').replace_with(lambda path: path)
flexmock(module.os.environ).should_receive('get').with_args('XDG_RUNTIME_DIR').and_return(None)
flexmock(module.os.environ).should_receive('get').with_args('TMPDIR').and_return(None)
flexmock(module.os.environ).should_receive('get').with_args('TEMP').and_return('/tmp')
assert module.get_borgmatic_runtime_directory({}) == '/tmp/./borgmatic'