Compare commits

8 Commits

View File

@@ -44,16 +44,14 @@ def get_machine_id():
### GLOBAL CONFIGURATION ###
# Container names treated as special instances for database backups
SPECIAL_INSTANCES = ['central-mariadb', 'central-postgres']
DATABASE_CONTAINERS = ['central-mariadb', 'central-postgres']
# Images which do not require container stop for file backups
IMAGES_NO_STOP_REQUIRED = []
# Images to skip entirely
IMAGES_NO_BACKUP_REQUIRED = [
'redis',
'memcached'
]
IMAGES_NO_BACKUP_REQUIRED = []
# Compose dirs requiring hard restart
DOCKER_COMPOSE_HARD_RESTART_REQUIRED = ['mailu']
@@ -70,7 +68,7 @@ VERSION_DIR = create_version_directory()
def get_instance(container):
"""Extract the database instance name based on container name."""
if container in SPECIAL_INSTANCES:
if container in DATABASE_CONTAINERS:
instance_name = container
else:
instance_name = re.split("(_|-)(database|db|postgres)", container)[0]
@@ -224,15 +222,37 @@ def change_containers_status(containers, status):
print(f"No containers to {status}.")
def is_image_whitelisted(container, images):
info = (container)[0]
return any(img in info for img in images)
"""
Return True if the container's image matches any of the whitelist patterns.
Also prints out the image name and the match result.
"""
# fetch the image (e.g. "nextcloud:23-fpm-alpine")
info = get_image_info(container)[0]
# check against each pattern
whitelisted = any(pattern in info for pattern in images)
# log the result
print(f"Container {container!r} → image {info!r} → whitelisted? {whitelisted}", flush=True)
return whitelisted
def is_container_stop_required(containers):
"""Check if any of the containers are using images that are not whitelisted."""
return any(
not is_image_whitelisted(c, IMAGES_NO_STOP_REQUIRED)
for c in containers
)
"""
Check if any of the containers are using images that are not whitelisted.
If so, print them out and return True; otherwise return False.
"""
# Find all containers whose image isnt on the whitelist
not_whitelisted = [
c for c in containers
if not is_image_whitelisted(c, IMAGES_NO_STOP_REQUIRED)
]
if not_whitelisted:
print(f"Containers requiring stop because they are not whitelisted: {', '.join(not_whitelisted)}")
return True
return False
def create_volume_directory(volume_name):
"""Create necessary directories for backup."""
@@ -311,7 +331,7 @@ def handle_docker_compose_services(parent_directory):
print(f"No docker-compose.yml found in {dir_path}. Skipping.")
def main():
global SPECIAL_INSTANCES, IMAGES_NO_STOP_REQUIRED
global DATABASE_CONTAINERS, IMAGES_NO_STOP_REQUIRED
parser = argparse.ArgumentParser(description='Backup Docker volumes.')
parser.add_argument('--everything', action='store_true',
help='Force file backup for all volumes and additional execute database dumps')
@@ -320,21 +340,30 @@ def main():
parser.add_argument('--compose-dir', type=str, required=True,
help='Path to the parent directory containing docker-compose setups')
parser.add_argument(
'--special-instances',
'--database-containers',
nargs='+',
default=SPECIAL_INSTANCES,
required=True,
help='List of container names treated as special instances for database backups'
)
parser.add_argument(
'--images-no-stop-required',
nargs='+',
required=True,
help='List of image names for which containers should not be stopped during file backup'
)
parser.add_argument(
'--images-no-backup-required',
nargs='+',
help='List of image names for which no backup should be performed (optional)'
)
args = parser.parse_args()
SPECIAL_INSTANCES = args.special_instances
DATABASE_CONTAINERS = args.database_containers
IMAGES_NO_STOP_REQUIRED = args.images_no_stop_required
if args.images_no_backup_required is not None:
global IMAGES_NO_BACKUP_REQUIRED
IMAGES_NO_BACKUP_REQUIRED = args.images_no_backup_required
print('Start volume backups...')
print('💾 Start volume backups...', flush=True)
volume_names = execute_shell_command("docker volume ls --format '{{.Name}}'")
for volume_name in volume_names:
print(f'Start backup routine for volume: {volume_name}')