parent
9eb050173f
commit
3a19623351
9 changed files with 54 additions and 16 deletions
|
@ -48,6 +48,7 @@ services:
|
||||||
image: mariadb:10
|
image: mariadb:10
|
||||||
labels:
|
labels:
|
||||||
restic-compose-backup.mariadb: true
|
restic-compose-backup.mariadb: true
|
||||||
|
restic-compose-backup.tags: db,test
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_ROOT_PASSWORD=my-secret-pw
|
- MYSQL_ROOT_PASSWORD=my-secret-pw
|
||||||
- MYSQL_DATABASE=mydb
|
- MYSQL_DATABASE=mydb
|
||||||
|
@ -71,6 +72,7 @@ services:
|
||||||
image: itzg/minecraft-server
|
image: itzg/minecraft-server
|
||||||
labels:
|
labels:
|
||||||
restic-compose-backup.minecraft: true
|
restic-compose-backup.minecraft: true
|
||||||
|
restic-compose-backup.tags: "test,foo,bar"
|
||||||
restic-compose-backup.volumes.include: "minecraft"
|
restic-compose-backup.volumes.include: "minecraft"
|
||||||
environment:
|
environment:
|
||||||
- RCON_PASSWORD=minecraft
|
- RCON_PASSWORD=minecraft
|
||||||
|
|
|
@ -316,6 +316,7 @@ def cleanup(config, containers):
|
||||||
config.keep_monthly,
|
config.keep_monthly,
|
||||||
config.keep_yearly,
|
config.keep_yearly,
|
||||||
config.keep_tags,
|
config.keep_tags,
|
||||||
|
config.filter_tags
|
||||||
)
|
)
|
||||||
logger.info('Prune stale data freeing storage space')
|
logger.info('Prune stale data freeing storage space')
|
||||||
prune_result = restic.prune(config.repository)
|
prune_result = restic.prune(config.repository)
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Config:
|
||||||
self.keep_monthly = os.environ.get('KEEP_MONTHLY') or "12"
|
self.keep_monthly = os.environ.get('KEEP_MONTHLY') or "12"
|
||||||
self.keep_yearly = os.environ.get('KEEP_YEARLY') or "3"
|
self.keep_yearly = os.environ.get('KEEP_YEARLY') or "3"
|
||||||
self.keep_tags = os.environ.get('KEEP_TAGS') or "keep"
|
self.keep_tags = os.environ.get('KEEP_TAGS') or "keep"
|
||||||
|
self.filter_tags = os.environ.get('FILTER_TAGS') or ""
|
||||||
|
|
||||||
if check:
|
if check:
|
||||||
self.check()
|
self.check()
|
||||||
|
|
|
@ -186,6 +186,11 @@ class Container:
|
||||||
"""Is this container the running backup process?"""
|
"""Is this container the running backup process?"""
|
||||||
return self.get_label(self.backup_process_label) == 'True'
|
return self.get_label(self.backup_process_label) == 'True'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tags(self) -> str:
|
||||||
|
"""Gets all backup tags"""
|
||||||
|
return self.get_label(enums.LABEL_RESTIC_TAGS)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_running(self) -> bool:
|
def is_running(self) -> bool:
|
||||||
"""bool: Is the container running?"""
|
"""bool: Is the container running?"""
|
||||||
|
|
|
@ -54,6 +54,7 @@ class MariadbContainer(Container):
|
||||||
config.repository,
|
config.repository,
|
||||||
self.backup_destination_path(),
|
self.backup_destination_path(),
|
||||||
self.dump_command(),
|
self.dump_command(),
|
||||||
|
tags=self.tags
|
||||||
)
|
)
|
||||||
|
|
||||||
def backup_destination_path(self) -> str:
|
def backup_destination_path(self) -> str:
|
||||||
|
@ -115,6 +116,7 @@ class MysqlContainer(Container):
|
||||||
config.repository,
|
config.repository,
|
||||||
self.backup_destination_path(),
|
self.backup_destination_path(),
|
||||||
self.dump_command(),
|
self.dump_command(),
|
||||||
|
tags=self.tags
|
||||||
)
|
)
|
||||||
|
|
||||||
def backup_destination_path(self) -> str:
|
def backup_destination_path(self) -> str:
|
||||||
|
@ -175,6 +177,7 @@ class PostgresContainer(Container):
|
||||||
config.repository,
|
config.repository,
|
||||||
self.backup_destination_path(),
|
self.backup_destination_path(),
|
||||||
self.dump_command(),
|
self.dump_command(),
|
||||||
|
tags=self.tags
|
||||||
)
|
)
|
||||||
|
|
||||||
def backup_destination_path(self) -> str:
|
def backup_destination_path(self) -> str:
|
||||||
|
|
|
@ -64,7 +64,7 @@ class MinecraftContainer(Container):
|
||||||
for mount in self.filter_mounts():
|
for mount in self.filter_mounts():
|
||||||
backup_data = self.get_volume_backup_destination(mount, '/minecraft')
|
backup_data = self.get_volume_backup_destination(mount, '/minecraft')
|
||||||
logger.info('Backing up %s', mount.source)
|
logger.info('Backing up %s', mount.source)
|
||||||
vol_result = restic.backup_files(config.repository, source=backup_data)
|
vol_result = restic.backup_files(config.repository, source=backup_data, tags=self.tags)
|
||||||
logger.debug('Minecraft backup exit code: %s', vol_result)
|
logger.debug('Minecraft backup exit code: %s', vol_result)
|
||||||
if vol_result != 0:
|
if vol_result != 0:
|
||||||
logger.error('Minecraft backup exited with non-zero code: %s', vol_result)
|
logger.error('Minecraft backup exited with non-zero code: %s', vol_result)
|
||||||
|
|
|
@ -10,4 +10,6 @@ LABEL_MARIADB_ENABLED = 'restic-compose-backup.mariadb'
|
||||||
|
|
||||||
LABEL_BACKUP_PROCESS = 'restic-compose-backup.process'
|
LABEL_BACKUP_PROCESS = 'restic-compose-backup.process'
|
||||||
|
|
||||||
LABEL_MINECRAFT_ENABLED = 'restic-compose-backup.minecraft'
|
LABEL_MINECRAFT_ENABLED = 'restic-compose-backup.minecraft'
|
||||||
|
|
||||||
|
LABEL_RESTIC_TAGS = 'restic-compose-backup.tags'
|
|
@ -4,7 +4,7 @@ Restic commands
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
from restic_compose_backup import commands
|
from restic_compose_backup import commands, utils
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -19,25 +19,29 @@ def init_repo(repository: str):
|
||||||
]))
|
]))
|
||||||
|
|
||||||
|
|
||||||
def backup_files(repository: str, source='/volumes'):
|
def backup_files(repository: str, source='/volumes', tags=''):
|
||||||
return commands.run(restic(repository, [
|
args = [
|
||||||
"--verbose",
|
"--verbose",
|
||||||
"backup",
|
"backup",
|
||||||
source,
|
source
|
||||||
]))
|
]
|
||||||
|
args.extend(utils.format_tags(tags))
|
||||||
|
return commands.run(restic(repository, args))
|
||||||
|
|
||||||
|
|
||||||
def backup_from_stdin(repository: str, filename: str, source_command: List[str]):
|
def backup_from_stdin(repository: str, filename: str, source_command: List[str], tags=''):
|
||||||
"""
|
"""
|
||||||
Backs up from stdin running the source_command passed in.
|
Backs up from stdin running the source_command passed in.
|
||||||
It will appear in restic with the filename (including path) passed in.
|
It will appear in restic with the filename (including path) passed in.
|
||||||
"""
|
"""
|
||||||
dest_command = restic(repository, [
|
args = [
|
||||||
'backup',
|
'backup',
|
||||||
'--stdin',
|
'--stdin',
|
||||||
'--stdin-filename',
|
'--stdin-filename',
|
||||||
filename,
|
filename,
|
||||||
])
|
]
|
||||||
|
args.extend(utils.format_tags(tags))
|
||||||
|
dest_command = restic(repository, args)
|
||||||
|
|
||||||
# pipe source command into dest command
|
# pipe source command into dest command
|
||||||
source_process = Popen(source_command, stdout=PIPE, bufsize=65536)
|
source_process = Popen(source_command, stdout=PIPE, bufsize=65536)
|
||||||
|
@ -75,8 +79,8 @@ def is_initialized(repository: str) -> bool:
|
||||||
return commands.run(restic(repository, ["snapshots", '--last'])) == 0
|
return commands.run(restic(repository, ["snapshots", '--last'])) == 0
|
||||||
|
|
||||||
|
|
||||||
def forget(repository: str, keeplast: str, hourly: str, daily: str, weekly: str, monthly: str, yearly: str, tags: str):
|
def forget(repository: str, keeplast: str, hourly: str, daily: str, weekly: str, monthly: str, yearly: str, keep_tags='', filter_tags=''):
|
||||||
return commands.run(restic(repository, [
|
args = [
|
||||||
'forget',
|
'forget',
|
||||||
'--group-by',
|
'--group-by',
|
||||||
'paths,tags',
|
'paths,tags',
|
||||||
|
@ -91,10 +95,11 @@ def forget(repository: str, keeplast: str, hourly: str, daily: str, weekly: str,
|
||||||
'--keep-monthly',
|
'--keep-monthly',
|
||||||
monthly,
|
monthly,
|
||||||
'--keep-yearly',
|
'--keep-yearly',
|
||||||
yearly,
|
yearly
|
||||||
'--keep-tag',
|
]
|
||||||
tags,
|
args.extend(utils.format_tags(keep_tags, '--keep-tag'))
|
||||||
]))
|
args.extend(utils.format_tags(filter_tags))
|
||||||
|
return commands.run(restic(repository, args))
|
||||||
|
|
||||||
|
|
||||||
def prune(repository: str):
|
def prune(repository: str):
|
||||||
|
|
|
@ -82,6 +82,25 @@ def strip_root(path):
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def format_tags(tags: str, arg = "--tag") -> List[str]:
|
||||||
|
"""
|
||||||
|
Takes a comma separated list of tags.
|
||||||
|
Splits them and appends --tag to each tag.
|
||||||
|
Use the output as the command line argument for the restic cli.
|
||||||
|
Example: foo,bar,test becomes --tag foo --tag bar --tag test
|
||||||
|
"""
|
||||||
|
if not tags:
|
||||||
|
return []
|
||||||
|
|
||||||
|
tags = tags.strip()
|
||||||
|
splitTags = tags.split(",")
|
||||||
|
output = []
|
||||||
|
for tag in splitTags:
|
||||||
|
tag = tag.strip()
|
||||||
|
if tag:
|
||||||
|
output.extend([arg, tag])
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def environment(name, value):
|
def environment(name, value):
|
||||||
|
|
Loading…
Add table
Reference in a new issue