Coverage for src/rsnapshot_docker_compose_backup/config/abstract_config.py: 34%
86 statements
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-07 01:03 +0200
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-07 01:03 +0200
1import configparser
2import os
3from pathlib import Path
4import re
5from abc import ABC, abstractmethod
6from typing import Any, Callable, Dict, List, Union
8from rsnapshot_docker_compose_backup.utils.regex import CaseInsensitiveRe
9from rsnapshot_docker_compose_backup.structure.volume import Volume
12class AbstractConfig(ABC):
13 actionSection = "actions"
14 varSection = "vars"
15 backupOrder = [
16 "runtime_backup",
17 "pre_stop",
18 "stop",
19 "pre_backup",
20 "backup",
21 "post_backup",
22 "restart",
23 "post_restart",
24 ]
26 def __init__(self, config_path: Path, name: str):
27 self.enabled_actions: Dict[str, bool] = {}
28 self.backup_steps: Dict[str, str] = {}
29 self.vars: Dict[str, Union[str, List[Volume]]] = {}
30 for step in self.backupOrder:
31 self.backup_steps[step] = ""
32 self._load_config_file(config_path, name)
33 self._init_vars(str(config_path))
35 def _init_vars(self, config_path: str) -> None:
36 self.vars["$dockerComposeFile"] = config_path
38 def _load_config_file(self, config_path: Path, section_name: str) -> None:
39 section_name = section_name.lower()
40 config_file = configparser.ConfigParser(allow_no_value=True)
41 config_file.SECTCRE = CaseInsensitiveRe(
42 re.compile(r"\[ *(?P<header>[^]]+?) *]")
43 ) # type: ignore
44 if os.path.isfile(config_path):
45 config_file.read(config_path)
46 if not config_file.sections():
47 raise Exception("The Config for {} has no Sections".format(config_path))
48 for step in self.backup_steps:
49 if config_file.has_option(section_name, step):
50 self.backup_steps[step] = (
51 config_file.get(section_name, step).strip() + "\n"
52 )
53 actions_section = self.actions_name(section_name)
54 if config_file.has_section(actions_section):
55 for action in config_file.options(actions_section):
56 val = config_file.get(actions_section, action, fallback=None)
57 use = val is None or val.lower() in {"true"}
58 self.enabled_actions[action] = use
59 vars_section = self.vars_name(section_name)
60 if config_file.has_section(vars_section):
61 for var in config_file.options(vars_section):
62 val = config_file.get(vars_section, var)
63 self.vars["${}".format(var)] = val
65 def _resolve_vars(
66 self, cmd: str, variables: Dict[str, Union[str, List[Volume]]]
67 ) -> str:
68 for var in variables.keys():
69 if var.lower() in cmd.lower():
70 replace_function = _replace_var.get(type(variables[var]))
71 if not replace_function:
72 raise Exception("Illegal Type")
73 cmd = replace_function(cmd, var, variables[var])
74 return cmd
76 @abstractmethod
77 def get_step(self, step: str) -> str:
78 pass
80 @staticmethod
81 def _create_subsection(super_section: str, sub_section: str) -> str:
82 return "{}.{}".format(super_section, sub_section)
84 @staticmethod
85 def actions_name(section_name: str) -> str:
86 return AbstractConfig._create_subsection(
87 section_name, AbstractConfig.actionSection
88 )
90 @staticmethod
91 def vars_name(section_name: str) -> str:
92 return AbstractConfig._create_subsection(
93 section_name, AbstractConfig.varSection
94 )
97def ireplace(old: str, new: str, text: str) -> str:
98 idx = 0
99 while idx < len(text):
100 index_l = text.lower().find(old.lower(), idx)
101 if index_l == -1:
102 return text
103 text = text[:index_l] + new + text[index_l + len(old) :]
104 idx = index_l + len(new)
105 return text
108def _replace_list(cmd: str, var: str, val: List[Union[List[Any], str, Volume]]) -> str:
109 result: str = ""
110 for i in val:
111 result += str(_replace_var[type(i)](cmd, var, i)) + "\n"
112 return result
115def _replace_str(cmd: str, var: str, val: str) -> str:
116 return ireplace(var, val, cmd)
119def _replace_volume(cmd: str, var: str, volume: Volume) -> str:
120 tmp = _replace_str(cmd, var + ".name", volume.name)
121 tmp = _replace_str(tmp, var + ".path", volume.path)
122 tmp = _replace_str(tmp, var, volume.path)
123 return tmp
126_replace_var: Dict[type, Callable[..., str]] = {
127 list: _replace_list,
128 str: _replace_str,
129 Volume: _replace_volume,
130}