Compare commits
11 Commits
5b8221ef77
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0086878d57 | ||
|
|
2ed9f3345d | ||
|
|
50dd0df84f | ||
|
|
e17894e49c | ||
|
|
326d2c0910 | ||
|
|
08e02d12db | ||
|
|
80842bc09c | ||
|
|
4e7fa8c87a | ||
|
|
2e2a777b11 | ||
|
|
3957e0c869 | ||
|
|
cae3f2bbf4 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
*.egg-info
|
*.egg-info
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
.env
|
||||||
|
key
|
||||||
@@ -7,11 +7,11 @@ Purpose:
|
|||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
Recommended to put these in the relevant `~/.bashrc` file
|
Put these in a `.env` file in the directory that the backup will be run from.
|
||||||
|
|
||||||
| Env Variable | Description |
|
| Env Variable | Description |
|
||||||
| ------------------- | ------------------------------------------------------------------------------------------ |
|
|---------------------|--------------------------------------------------------------------------------------------|
|
||||||
| `HOSTNAME` | Network hostname of where the backup is running |
|
| `HOSTNAME` | Network hostname of where the backup is running. Used to tag the backups in restic |
|
||||||
| `BACKUP_DIR` | Directory to back up |
|
| `BACKUP_DIR` | Directory to back up |
|
||||||
| `RESTIC_REPOSITORY` | Directory for the restic repository. This is usually on a mount point made from Proxmox |
|
| `RESTIC_REPOSITORY` | Directory for the restic repository. This is usually on a mount point made from Proxmox |
|
||||||
| `RESTIC_PASSWORD` | Password for the restic repository |
|
| `RESTIC_PASSWORD` | Password for the restic repository |
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ authors = [
|
|||||||
license = { file = "LICENSE" }
|
license = { file = "LICENSE" }
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|
||||||
dependencies = ["rich", "requests", "click"]
|
dependencies = ["rich", "requests", "click", "docker", "python-dotenv"]
|
||||||
|
|||||||
@@ -3,3 +3,5 @@ ruff
|
|||||||
rich
|
rich
|
||||||
requests
|
requests
|
||||||
click
|
click
|
||||||
|
docker
|
||||||
|
python-dotenv
|
||||||
@@ -81,6 +81,7 @@ def main(
|
|||||||
logging.getLogger('urllib3.connectionpool').setLevel('WARNING')
|
logging.getLogger('urllib3.connectionpool').setLevel('WARNING')
|
||||||
|
|
||||||
if project is not None and services is not None:
|
if project is not None and services is not None:
|
||||||
|
logger.debug(f'Using project {project} and stopping services: {services}')
|
||||||
decorator = manage_containers(project=project, services=services.split(','))
|
decorator = manage_containers(project=project, services=services.split(','))
|
||||||
func = decorator(run)
|
func = decorator(run)
|
||||||
else:
|
else:
|
||||||
@@ -96,4 +97,10 @@ def main(
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
dotenv_file = Path.cwd() / '.env'
|
||||||
|
print(dotenv_file)
|
||||||
|
load_dotenv(dotenv_path=dotenv_file)
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ def manage_containers(project: str, services: list[str]):
|
|||||||
try:
|
try:
|
||||||
project_containers = (
|
project_containers = (
|
||||||
c
|
c
|
||||||
for c in client.containers.list()
|
for c in client.containers.list(all=True)
|
||||||
if c.labels['com.docker.compose.project'] == project
|
if c.labels.get('com.docker.compose.project', False)
|
||||||
)
|
)
|
||||||
service_dict = {
|
service_dict: dict[str, Container] = {
|
||||||
c.labels['com.docker.compose.service']: c for c in project_containers
|
service: c for c in project_containers
|
||||||
|
if (service := c.labels.get('com.docker.compose.service', False))
|
||||||
}
|
}
|
||||||
containers: list[Container] = [service_dict[s] for s in services]
|
containers: list[Container] = [service_dict[s] for s in services]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from rich.logging import RichHandler
|
|||||||
from restic import size, snapshots
|
from restic import size, snapshots
|
||||||
from restic.console import console, logger
|
from restic.console import console, logger
|
||||||
from restic.loki import send_to_loki
|
from restic.loki import send_to_loki
|
||||||
|
from restic.prune import prune
|
||||||
|
|
||||||
|
|
||||||
class KeepStrategy(BaseModel):
|
class KeepStrategy(BaseModel):
|
||||||
@@ -85,9 +86,16 @@ def main(loki_url: str, dry_run: bool, **kwargs):
|
|||||||
except Exception:
|
except Exception:
|
||||||
console.print_exception()
|
console.print_exception()
|
||||||
else:
|
else:
|
||||||
snapshots.snapshot(loki_url)
|
prune(loki_url, dry_run)
|
||||||
size.get_size(loki_url)
|
if not dry_run:
|
||||||
|
snapshots.snapshot(loki_url)
|
||||||
|
size.get_size(loki_url)
|
||||||
|
else:
|
||||||
|
logger.debug('Skipping getting size because of dry run')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from restic.console import console, logger
|
|||||||
from restic.loki import send_to_loki
|
from restic.loki import send_to_loki
|
||||||
|
|
||||||
field_regex = re.compile(
|
field_regex = re.compile(
|
||||||
r'^(?P<name>[\w ]+):\s+(?P<blobs>\d+) blobs \/ (?P<size>\d+(\.\d+)? (M|G)iB)', re.MULTILINE
|
r'^(?P<name>[\w ]+):\s+(?P<blobs>\d+) blobs \/ (?P<size>\d+(\.\d+)? (?:Mi|Gi)?B)', re.MULTILINE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +23,8 @@ def convert_size(size_str: str) -> int:
|
|||||||
scale = 8589934592
|
scale = 8589934592
|
||||||
elif size_str.endswith('MiB'):
|
elif size_str.endswith('MiB'):
|
||||||
scale = 8388608
|
scale = 8388608
|
||||||
|
else:
|
||||||
|
scale = 1
|
||||||
return int(round(base_size * scale))
|
return int(round(base_size * scale))
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ def prune(loki_url: str = None, dry_run: bool = False):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
d = {f['name']: {'blobs': f['blobs'], 'size': f['size']} for f in field_gen(result.stdout)}
|
d = {f['name']: {'blobs': f['blobs'], 'size': f['size']} for f in field_gen(result.stdout)}
|
||||||
console.print(d)
|
logger.debug(json.dumps(d, indent=4))
|
||||||
|
|
||||||
if loki_url is not None and not dry_run:
|
if loki_url is not None and not dry_run:
|
||||||
send_to_loki(loki_url, line=json.dumps(d), backup='prune')
|
send_to_loki(loki_url, line=json.dumps(d), backup='prune')
|
||||||
|
|||||||
@@ -35,4 +35,7 @@ def main(loki_url: str = None):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user