#!/usr/bin/env python # file backup.py # author Florent Guiotte # version 0.0 # date 10 août 2024 """Abstract doc. """ import yaml from pathlib import Path import subprocess from datetime import datetime COMPOSE_PATH = Path('docker-compose.yml') BACKUP_PATH = Path('./data/bkp') VOLUME_PREFIX = 'docker_' class UnionFind: def __init__(self): self.parent = {} def make(self, service): self.parent.setdefault(service, service) def find(self, service): """return root""" if self.parent[service] != service: self.parent[service] = self.find(self.parent[service]) return self.parent[service] def union(self, service1, service2): root1 = self.find(service1) root2 = self.find(service2) if root1 != root2: self.parent[root2] = root1 # compress! def build_services_graph(services): uf = UnionFind() for service in services: uf.make(service) for dependency in services[service].get('depends_on', []): uf.make(dependency) uf.union(service, dependency) return uf def group_services(services, graph): grouped_services = {} for service in services: root = graph.find(service) if root not in grouped_services: grouped_services[root] = {'services': []} grouped_services[root]['services'].append(service) return grouped_services def group_volumes(services, volumes, services_group): for group_name, group in services_group.items(): group_volumes = group.setdefault('volumes', []) for service in group['services']: for volume in [v.split(':')[0] for v in services[service]['volumes']]: if volume in volumes: group_volumes += [volume] return services_group def backup(volume): current_date = datetime.now() date_string = current_date.strftime("%Y-%m-%d") archive_name = f'{date_string}_{volume}.tar' print(f'backup volume {volume} to {BACKUP_PATH}/{archive_name}') subprocess.run(f'docker run --rm --volume {VOLUME_PREFIX}{volume}:/data --volume {BACKUP_PATH.resolve()}:/bkp ubuntu tar -cf /bkp/{archive_name} -C /data .'.split()) def run_docker_compose(cmd): subprocess.run(f'docker compose {cmd}'.split()) if __name__ == '__main__': with COMPOSE_PATH.open() as cf: compose = yaml.safe_load(cf) services = compose['services'] volumes = compose['volumes'] services_graph = build_services_graph(services) services_group = group_services(services, services_graph) services_group = group_volumes(services, volumes, services_group) for group_name, group in services_group.items(): print(f'Service group {group_name} ', end='') if not group['volumes']: print('no volumes') continue print('run backup...') run_docker_compose(f'stop {" ".join(group["services"])}') for volume in group['volumes']: backup(volume) run_docker_compose(f'start {" ".join(group["services"])}')