declarative-data-backup 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/PKG-INFO +17 -11
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/README.md +16 -10
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/pyproject.toml +1 -1
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/backup.py +7 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/borg.py +13 -7
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/environment.py +10 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/PKG-INFO +17 -11
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/tests/test_backup.py +4 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/setup.cfg +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/__init__.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/__main__.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/__init__.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/archive_naming.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/borg/borg_cli.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/__init__.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/__init__.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/models.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/utils.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/yaml_parser.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/backup_config/yaml_raw_loader.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/config/logging.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup/errors.py +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/SOURCES.txt +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/dependency_links.txt +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/requires.txt +0 -0
- {declarative_data_backup-0.2.1 → declarative_data_backup-0.3.0}/src/declarative_data_backup.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: declarative-data-backup
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Declarative Borg-based data backups
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -149,6 +149,8 @@ Optional variables:
|
|
|
149
149
|
| --- | --- |
|
|
150
150
|
| `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
|
|
151
151
|
| `BORG_EXIT_CODES` | Borg exit-code mode: `legacy` or `modern`. |
|
|
152
|
+
| `BACKUP_PROGRESS` | Set to `true` to enable Borg's live progress output during archive creation. Defaults to `false` to limit log volume. |
|
|
153
|
+
| `BACKUP_VERBOSE` | Set to `true` to show Borg's per-file create and per-archive prune output. Defaults to `false` for concise logs. |
|
|
152
154
|
|
|
153
155
|
Provide credentials through a secret manager or protected files; do not put
|
|
154
156
|
passphrases, private keys, or credentials in the YAML file, source control, or
|
|
@@ -161,7 +163,10 @@ python -m declarative_data_backup
|
|
|
161
163
|
|
|
162
164
|
The command exits with status `0` on success, nonzero on configuration or Borg
|
|
163
165
|
failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
|
|
164
|
-
the run fails and pruning is skipped.
|
|
166
|
+
the run fails and pruning is skipped. Logs include group and source start/end
|
|
167
|
+
messages, while Borg's per-file and per-archive output is disabled by default.
|
|
168
|
+
Set `BACKUP_VERBOSE=true` for detailed Borg output or `BACKUP_PROGRESS=true`
|
|
169
|
+
for live progress during archive creation; either can increase log volume.
|
|
165
170
|
|
|
166
171
|
## Configuration reference
|
|
167
172
|
|
|
@@ -289,23 +294,24 @@ container integration test.
|
|
|
289
294
|
|
|
290
295
|
## Testing
|
|
291
296
|
|
|
292
|
-
Run the unit tests:
|
|
297
|
+
Run the unit tests, integration tests, or both:
|
|
293
298
|
|
|
294
299
|
```shell
|
|
295
|
-
|
|
300
|
+
make test-unit
|
|
301
|
+
make test-integration
|
|
302
|
+
make test
|
|
296
303
|
```
|
|
297
304
|
|
|
298
|
-
The
|
|
305
|
+
The integration target opts in to both local Borg tests (which use temporary
|
|
306
|
+
encrypted repositories) and the Docker SSH test (which uses disposable
|
|
307
|
+
containers and a temporary repository). It does not use production credentials
|
|
308
|
+
or BorgBase. Docker must be installed and its daemon available.
|
|
309
|
+
|
|
310
|
+
To run either integration suite individually:
|
|
299
311
|
|
|
300
312
|
```shell
|
|
301
313
|
RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
|
|
302
314
|
tests/integration/test_local_borg_integration.py
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
The opt-in Docker SSH integration test uses disposable containers and a
|
|
306
|
-
temporary repository; it does not use production credentials or BorgBase:
|
|
307
|
-
|
|
308
|
-
```shell
|
|
309
315
|
RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
|
|
310
316
|
tests/integration/test_docker_integration.py
|
|
311
317
|
```
|
|
@@ -141,6 +141,8 @@ Optional variables:
|
|
|
141
141
|
| --- | --- |
|
|
142
142
|
| `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
|
|
143
143
|
| `BORG_EXIT_CODES` | Borg exit-code mode: `legacy` or `modern`. |
|
|
144
|
+
| `BACKUP_PROGRESS` | Set to `true` to enable Borg's live progress output during archive creation. Defaults to `false` to limit log volume. |
|
|
145
|
+
| `BACKUP_VERBOSE` | Set to `true` to show Borg's per-file create and per-archive prune output. Defaults to `false` for concise logs. |
|
|
144
146
|
|
|
145
147
|
Provide credentials through a secret manager or protected files; do not put
|
|
146
148
|
passphrases, private keys, or credentials in the YAML file, source control, or
|
|
@@ -153,7 +155,10 @@ python -m declarative_data_backup
|
|
|
153
155
|
|
|
154
156
|
The command exits with status `0` on success, nonzero on configuration or Borg
|
|
155
157
|
failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
|
|
156
|
-
the run fails and pruning is skipped.
|
|
158
|
+
the run fails and pruning is skipped. Logs include group and source start/end
|
|
159
|
+
messages, while Borg's per-file and per-archive output is disabled by default.
|
|
160
|
+
Set `BACKUP_VERBOSE=true` for detailed Borg output or `BACKUP_PROGRESS=true`
|
|
161
|
+
for live progress during archive creation; either can increase log volume.
|
|
157
162
|
|
|
158
163
|
## Configuration reference
|
|
159
164
|
|
|
@@ -281,23 +286,24 @@ container integration test.
|
|
|
281
286
|
|
|
282
287
|
## Testing
|
|
283
288
|
|
|
284
|
-
Run the unit tests:
|
|
289
|
+
Run the unit tests, integration tests, or both:
|
|
285
290
|
|
|
286
291
|
```shell
|
|
287
|
-
|
|
292
|
+
make test-unit
|
|
293
|
+
make test-integration
|
|
294
|
+
make test
|
|
288
295
|
```
|
|
289
296
|
|
|
290
|
-
The
|
|
297
|
+
The integration target opts in to both local Borg tests (which use temporary
|
|
298
|
+
encrypted repositories) and the Docker SSH test (which uses disposable
|
|
299
|
+
containers and a temporary repository). It does not use production credentials
|
|
300
|
+
or BorgBase. Docker must be installed and its daemon available.
|
|
301
|
+
|
|
302
|
+
To run either integration suite individually:
|
|
291
303
|
|
|
292
304
|
```shell
|
|
293
305
|
RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
|
|
294
306
|
tests/integration/test_local_borg_integration.py
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
The opt-in Docker SSH integration test uses disposable containers and a
|
|
298
|
-
temporary repository; it does not use production credentials or BorgBase:
|
|
299
|
-
|
|
300
|
-
```shell
|
|
301
307
|
RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
|
|
302
308
|
tests/integration/test_docker_integration.py
|
|
303
309
|
```
|
|
@@ -70,8 +70,14 @@ def run_backup(
|
|
|
70
70
|
run_id,
|
|
71
71
|
definition.excludes,
|
|
72
72
|
)
|
|
73
|
+
logger.info("Backup group '%s' completed.", definition.name)
|
|
73
74
|
|
|
74
75
|
for definition in definitions:
|
|
76
|
+
logger.info(
|
|
77
|
+
"Starting retention pruning for group '%s' (%d sources).",
|
|
78
|
+
definition.name,
|
|
79
|
+
len(definition.sources),
|
|
80
|
+
)
|
|
75
81
|
for folder in definition.sources:
|
|
76
82
|
prune(
|
|
77
83
|
folder,
|
|
@@ -79,6 +85,7 @@ def run_backup(
|
|
|
79
85
|
definition.name,
|
|
80
86
|
settings,
|
|
81
87
|
)
|
|
88
|
+
logger.info("Retention pruning for group '%s' completed.", definition.name)
|
|
82
89
|
|
|
83
90
|
|
|
84
91
|
def main(environ: Mapping[str, str] | None = None) -> int:
|
|
@@ -41,11 +41,14 @@ def backup_folder(
|
|
|
41
41
|
logger.info("Starting backup for %s as archive %s.", context, name)
|
|
42
42
|
args = [
|
|
43
43
|
"create",
|
|
44
|
-
"--verbose",
|
|
45
44
|
"--stats",
|
|
46
45
|
"--compression",
|
|
47
46
|
compression,
|
|
48
47
|
]
|
|
48
|
+
if settings.backup_verbose:
|
|
49
|
+
args.append("--verbose")
|
|
50
|
+
if settings.backup_progress:
|
|
51
|
+
args.append("--progress")
|
|
49
52
|
for pattern in excludes:
|
|
50
53
|
args.extend(("--exclude", pattern))
|
|
51
54
|
args.extend((f"{settings.borg_repo}::{name}", str(folder)))
|
|
@@ -85,17 +88,20 @@ def prune(
|
|
|
85
88
|
retention_days,
|
|
86
89
|
)
|
|
87
90
|
try:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
args = ["prune"]
|
|
92
|
+
if settings.backup_verbose:
|
|
93
|
+
args.extend(("--verbose", "--list"))
|
|
94
|
+
args.extend(
|
|
95
|
+
(
|
|
93
96
|
"--glob-archives",
|
|
94
97
|
selector,
|
|
95
98
|
"--keep-daily",
|
|
96
99
|
str(retention_days),
|
|
97
100
|
settings.borg_repo,
|
|
98
|
-
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
run_borg(
|
|
104
|
+
args,
|
|
99
105
|
settings,
|
|
100
106
|
"prune",
|
|
101
107
|
context,
|
|
@@ -17,6 +17,8 @@ class Environment:
|
|
|
17
17
|
borg_passcommand: str
|
|
18
18
|
borg_remote_path: str | None
|
|
19
19
|
borg_exit_codes: str | None
|
|
20
|
+
backup_progress: bool
|
|
21
|
+
backup_verbose: bool
|
|
20
22
|
|
|
21
23
|
@classmethod
|
|
22
24
|
def from_env(
|
|
@@ -46,6 +48,12 @@ class Environment:
|
|
|
46
48
|
borg_exit_codes = source.get("BORG_EXIT_CODES")
|
|
47
49
|
if borg_exit_codes not in (None, "legacy", "modern"):
|
|
48
50
|
fail("BORG_EXIT_CODES must be either 'legacy' or 'modern'.")
|
|
51
|
+
backup_progress_value = source.get("BACKUP_PROGRESS", "false").lower()
|
|
52
|
+
if backup_progress_value not in ("true", "false"):
|
|
53
|
+
fail("BACKUP_PROGRESS must be either 'true' or 'false'.")
|
|
54
|
+
backup_verbose_value = source.get("BACKUP_VERBOSE", "false").lower()
|
|
55
|
+
if backup_verbose_value not in ("true", "false"):
|
|
56
|
+
fail("BACKUP_VERBOSE must be either 'true' or 'false'.")
|
|
49
57
|
|
|
50
58
|
try:
|
|
51
59
|
server_root = required_path("SERVER_ROOT").resolve(strict=True)
|
|
@@ -62,6 +70,8 @@ class Environment:
|
|
|
62
70
|
borg_passcommand=required("BORG_PASSCOMMAND"),
|
|
63
71
|
borg_remote_path=source.get("BORG_REMOTE_PATH"),
|
|
64
72
|
borg_exit_codes=borg_exit_codes,
|
|
73
|
+
backup_progress=backup_progress_value == "true",
|
|
74
|
+
backup_verbose=backup_verbose_value == "true",
|
|
65
75
|
)
|
|
66
76
|
|
|
67
77
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: declarative-data-backup
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Declarative Borg-based data backups
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -149,6 +149,8 @@ Optional variables:
|
|
|
149
149
|
| --- | --- |
|
|
150
150
|
| `BORG_REMOTE_PATH` | Remote Borg executable path, passed as Borg's `--remote-path`. Leave unset to use Borg's default. |
|
|
151
151
|
| `BORG_EXIT_CODES` | Borg exit-code mode: `legacy` or `modern`. |
|
|
152
|
+
| `BACKUP_PROGRESS` | Set to `true` to enable Borg's live progress output during archive creation. Defaults to `false` to limit log volume. |
|
|
153
|
+
| `BACKUP_VERBOSE` | Set to `true` to show Borg's per-file create and per-archive prune output. Defaults to `false` for concise logs. |
|
|
152
154
|
|
|
153
155
|
Provide credentials through a secret manager or protected files; do not put
|
|
154
156
|
passphrases, private keys, or credentials in the YAML file, source control, or
|
|
@@ -161,7 +163,10 @@ python -m declarative_data_backup
|
|
|
161
163
|
|
|
162
164
|
The command exits with status `0` on success, nonzero on configuration or Borg
|
|
163
165
|
failure, and `130` when interrupted. Borg's exit code `1` indicates warnings:
|
|
164
|
-
the run fails and pruning is skipped.
|
|
166
|
+
the run fails and pruning is skipped. Logs include group and source start/end
|
|
167
|
+
messages, while Borg's per-file and per-archive output is disabled by default.
|
|
168
|
+
Set `BACKUP_VERBOSE=true` for detailed Borg output or `BACKUP_PROGRESS=true`
|
|
169
|
+
for live progress during archive creation; either can increase log volume.
|
|
165
170
|
|
|
166
171
|
## Configuration reference
|
|
167
172
|
|
|
@@ -289,23 +294,24 @@ container integration test.
|
|
|
289
294
|
|
|
290
295
|
## Testing
|
|
291
296
|
|
|
292
|
-
Run the unit tests:
|
|
297
|
+
Run the unit tests, integration tests, or both:
|
|
293
298
|
|
|
294
299
|
```shell
|
|
295
|
-
|
|
300
|
+
make test-unit
|
|
301
|
+
make test-integration
|
|
302
|
+
make test
|
|
296
303
|
```
|
|
297
304
|
|
|
298
|
-
The
|
|
305
|
+
The integration target opts in to both local Borg tests (which use temporary
|
|
306
|
+
encrypted repositories) and the Docker SSH test (which uses disposable
|
|
307
|
+
containers and a temporary repository). It does not use production credentials
|
|
308
|
+
or BorgBase. Docker must be installed and its daemon available.
|
|
309
|
+
|
|
310
|
+
To run either integration suite individually:
|
|
299
311
|
|
|
300
312
|
```shell
|
|
301
313
|
RUN_BORG_LOCAL_INTEGRATION=1 uv run pytest -m integration \
|
|
302
314
|
tests/integration/test_local_borg_integration.py
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
The opt-in Docker SSH integration test uses disposable containers and a
|
|
306
|
-
temporary repository; it does not use production credentials or BorgBase:
|
|
307
|
-
|
|
308
|
-
```shell
|
|
309
315
|
RUN_BORG_DOCKER_INTEGRATION=1 uv run pytest -m integration \
|
|
310
316
|
tests/integration/test_docker_integration.py
|
|
311
317
|
```
|
|
@@ -29,6 +29,7 @@ def _configure_preflight(monkeypatch):
|
|
|
29
29
|
def test_main_runs_all_creates_before_any_prune(
|
|
30
30
|
settings_factory,
|
|
31
31
|
monkeypatch,
|
|
32
|
+
caplog,
|
|
32
33
|
):
|
|
33
34
|
settings = settings_factory()
|
|
34
35
|
media = settings.server_root / "media"
|
|
@@ -62,6 +63,7 @@ def test_main_runs_all_creates_before_any_prune(
|
|
|
62
63
|
monkeypatch.setattr(
|
|
63
64
|
backup.uuid, "uuid4", lambda: type("UUID", (), {"hex": "a" * 32})()
|
|
64
65
|
)
|
|
66
|
+
caplog.set_level(logging.INFO)
|
|
65
67
|
|
|
66
68
|
assert backup.main(_settings_environment(settings)) == 0
|
|
67
69
|
assert [event[0] for event in events] == [
|
|
@@ -73,6 +75,8 @@ def test_main_runs_all_creates_before_any_prune(
|
|
|
73
75
|
assert events[0][3] == "zstd,5"
|
|
74
76
|
assert events[1][3] == "auto,zstd,22"
|
|
75
77
|
assert events[2][3] == 30
|
|
78
|
+
assert "Backup group 'media_backup' completed." in caplog.text
|
|
79
|
+
assert "Retention pruning for group 'media_backup' completed." in caplog.text
|
|
76
80
|
|
|
77
81
|
|
|
78
82
|
def test_main_warning_aborts_remaining_creates_and_all_pruning(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|