diff --git a/configuration.ini.example b/configuration.ini.example index 7199d4c..b58b05f 100644 --- a/configuration.ini.example +++ b/configuration.ini.example @@ -2,9 +2,9 @@ bindcludepattern=list of fixed strings bindexcludepattern=list of fixed strings composefile=path to the docker-compose file to parse +sshfolder=path to the .ssh folder to use [restic] remotehost= -remoteuser= repository= passwordfile= \ No newline at end of file diff --git a/main.py b/main.py index e5f75eb..6065b42 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,9 @@ import os.path - import docker import restic import yaml -import json import configparser +import argparse bindingIncludePattern = None @@ -15,13 +14,49 @@ repository = None passwordFile = None +def arguments_parser(): + ''' + récupère les arguments de la commande + :return: + ''' + parser = argparse.ArgumentParser( + prog='BackandUp', + description='Parse your docker-compose.yml file to automatically shut the containers, backup their volumes ' + 'and restart them. Dependencies between containers are respected.', + epilog='BackandUp 2023') + parser.add_argument('-c', '--config', action='store', default='./configuration.ini', + help='configuration file for BackandUp. By default it looks for a configuration.ini ' + 'in its folder. It must contains the configuration for the backup service ' + 'and can also contains the BackandUp own configuration.') + parser.add_argument('--compose', action='store', default='./docker-compose.yml', + help='compose file to parse. By default it looks for a docker-compose.yml in its folder.') + parser.add_argument('--sshfolder', action='store', default='~/.ssh', + help='ssh folder for sftp connection. By default it uses ~/.ssh.') + parser.add_argument('-x', '--bindexclude', action='store', default=None, + help='Patterns of volume or bind to exclude from the backup.') + parser.add_argument('-i', '--bindinclude', action='store', default=None, + help='Patterns of volume or bind to include from the backup. Using this argument, ' + 'only matching volumes and binds will be backed up.') + + args = parser.parse_args() + print(args) + print(vars(args)) + bindcludepattern = None + bindexcludepattern = None + configfile = None + composefile = "/root/compose/core/docker-compose.yml" + sshfolder = "/root/.ssh" + remotehost = None + repository = None + passwordfile = None + + def read_config_file(filepath: str = "configuration.ini"): parser = configparser.ConfigParser() parser.read(filepath) global bindingIncludePattern global bindingExcludePattern global remoteHost - global remoteUser global repository global passwordFile bindingIncludePattern = parser["backandup"]["bindincludepattern"] @@ -146,12 +181,13 @@ def order_services_by_dependencies(servicesdict: dict) -> list: return serviceslist -def backup_service(servicename: str, servicesdict: dict, composefilepath: str) -> bool: +def backup_services(servicename: str, servicesdict: dict, composefilepath: str) -> bool: ''' Pause les containers, lance la backup des dépendances si il y en a, lance sa propre backup, puis redémarre les containers. :param servicename: str :param servicesdict: dict + :param composefilepath: str :return: bool ''' @@ -185,43 +221,24 @@ def backup_service(servicename: str, servicesdict: dict, composefilepath: str) - return True -def yaml_file_to_container_list(filepath: str) -> list|None: +def volume_backup(itemtobackup: str) -> bool: ''' - Retourne le contenu d'un fichier yaml sous forme de dictionnaire - :param filepath: str - :return: dict|None + Exécute la sauvegarde du dossier/fichier bind ou volume du container. + :param itemtobackup: str + :return: bool ''' - pass + # il faut lancer l'équivalent de ceci : + # docker run --rm -v $(itemtobackup):/$(itemtobackup) -v $passwordfile:/$passwordfile -v $sshfolder:/root/.ssh \ + # --host $host restic/restic restic --password-file /$passwordfile -r $remotehost backup /$(itemtobackup) - # dependances = [] - # if "depends_on" in ycontent["services"][servicename]: - # dependances = ycontent["services"][servicename]["depends_on"] - # - # bindslist = [] - # volumeslist = [] - # for mount in ycontent["services"][servicename]["volumes"]: - # localpart = mount.split(":") - # # TODO: adapter avec pathlib pour gérer Windows - # # si un /, signifie qu'il s'agit d'un chemin et donc d'un bind mount, pas volume. - # if localpart.find("/") != -1: - # bindslist.append(localpart) - # else: - # volumeslist.append(localpart) - # - # containersid = os.popen(f"docker-compose --file {filepath} ps --quiet {servicename}").readlines() - # containersnames = os.popen(f"docker-compose --file {filepath} ps {servicename} | tail -n +3").readlines() - # - # containerslist = [] - # for i in range(0, len(containersid)): - # containerslist.append((containersnames[i], containersid[i])) - # - # for containerfound in containerslist: - # container = {"service": servicename, - # "name": containerfound[0], - # "id": containerfound[1], - # "binds": bindslist, - # "depends_on": dependances} + volumes = {sshfolder: {'bind': '/root/.ssh', 'mode': 'ro'}, + passwordfile: {'bind': '/.resticpass', 'mode': 'ro'}} + ctpath = itemtobackup + if itemtobackup.find("/") == -1 : + ctpath = f"/{itemtobackup}" + dockerclient = docker.from_env() + backuplog = dockerclient.containers.run("restic/restic", command="Lol") @@ -233,17 +250,18 @@ if __name__ == '__main__': bindcludepattern = None bindexcludepattern = None + configfile = None composefile = "/root/compose/core/docker-compose.yml" + sshfolder = "/root/.ssh" remotehost = None - remoteuser = None repository = None passwordfile = None - #read_config_file() + arguments_parser() - servicesDict = yaml_to_services_list(composefile) - servicesList = order_services_by_dependencies(servicesDict) - print("Backup process is starting") - for servicetobackup in reversed(servicesList): - print(f" - {servicetobackup}") - backup_service(servicetobackup, servicesDict, composefile) + # servicesDict = yaml_to_services_list(composefile) + # servicesList = order_services_by_dependencies(servicesDict) + # print("Backup process is starting") + # for servicetobackup in reversed(servicesList): + # print(f" - {servicetobackup}") + # backup_services(servicetobackup, servicesDict, composefile)