diff --git a/plugins/__init__.py b/plugins/__init__.py new file mode 100644 index 0000000..61c8274 --- /dev/null +++ b/plugins/__init__.py @@ -0,0 +1,4 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""A collection to ease the creation of docker compose files using ansible.""" diff --git a/plugins/module_utils/__init__.py b/plugins/module_utils/__init__.py new file mode 100644 index 0000000..f486faa --- /dev/null +++ b/plugins/module_utils/__init__.py @@ -0,0 +1,4 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""Module utilities for ez_compose modules.""" diff --git a/plugins/module_utils/common.py b/plugins/module_utils/common.py index f0fc26d..5677d5f 100644 --- a/plugins/module_utils/common.py +++ b/plugins/module_utils/common.py @@ -1,17 +1,21 @@ # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) +"""Common module utilities.""" + from __future__ import annotations import copy from dataclasses import asdict, dataclass, field, replace +from pathlib import Path from typing import Any, Callable import yaml from ansible.module_utils.basic import AnsibleModule PROJECTS_DIR = "/var/lib/ez_compose" -BASE_SERVICE_ARGS = { + +BASE_ARGS = { "project_name": { "type": "str", "required": True, @@ -20,59 +24,41 @@ BASE_SERVICE_ARGS = { "type": "str", "required": True, }, - "image": { - "type": "str", - }, - "state": { - "type": "str", - "default": "present", - "choices": ["present", "absent"], - }, - "defaults": { - "type": "dict", - }, -} -BASE_LABEL_ARGS = { - "project_name": { - "type": "str", - "required": True, - }, - "name": { - "type": "str", - "required": True, - }, - "state": { - "type": "str", - "default": "present", - "choices": ["present", "absent"], - }, } @dataclass(frozen=True) class Result: + """Module result object.""" + changed: bool = False diff: dict[str, Any] = field(default_factory=dict) -@dataclass(frozen=True) -class Settings: - projects_dir: str = "/usr/local/share/ez_compose/" +# @dataclass(frozen=True) +# class Settings: +# projects_dir: str = "/usr/local/share/ez_compose/" @dataclass(frozen=True) class State: - module: Any # Replace Any with the actual type of AnsibleModule if available + """Execution state object.""" + + module: AnsibleModule result: Result compose_filepath: str before: dict[str, Any] after: dict[str, Any] -def _recursive_update(default: dict[Any, Any], update: dict[Any, Any]) -> dict[Any, Any]: - for key in update: +def recursive_update( + default: dict[Any, Any], + update: dict[Any, Any], +) -> dict[Any, Any]: + """Recursively update a dictionary.""" + for key in update: # noqa: PLC0206 if isinstance(update[key], dict) and isinstance(default.get(key), dict): - default[key] = _recursive_update(default[key], update[key]) + default[key] = recursive_update(default[key], update[key]) elif isinstance(update[key], list) and isinstance(default.get(key), list): default_set = set(default[key]) @@ -86,10 +72,13 @@ def _recursive_update(default: dict[Any, Any], update: dict[Any, Any]) -> dict[A def get_state(module: AnsibleModule) -> State: - compose_filepath = f"{PROJECTS_DIR}/{module.params['project_name']}/docker-compose.yml" + """Create a new state object, loading the compose file into "before" if it exists.""" + compose_filepath = ( + f"{PROJECTS_DIR}/{module.params['project_name']}/docker-compose.yml" + ) try: - with open(compose_filepath, "r") as fp: + with Path(compose_filepath).open("r") as fp: before = yaml.safe_load(fp) except FileNotFoundError: @@ -100,92 +89,11 @@ def get_state(module: AnsibleModule) -> State: result=Result(), compose_filepath=compose_filepath, before=before, - after=before, + after={}, ) - -def apply_service_base(state: State) -> State: - service_name = state.module.params["name"] - project_name = state.module.params["project_name"] - image = state.module.params["image"] - - update: dict[str, Any] = { - "service_name": f"{project_name}_{service_name}", - "hostname": f"{project_name}_{service_name}", - "image": image, - "restart": "unless-stopped", - "environment": {}, - "labels": {}, - "volumes": [], - "networks": { - f"{project_name}_internal": None, - }, - } - - return update_service(state, update) - - -def set_service_defaults(state: State) -> State: - container_name = state.module.params["name"] - defaults = state.module.params["defaults"] - project = copy.deepcopy(state.after) - services = project["services"] - service = services[container_name] - - _ = _recursive_update(service, defaults) - - services.update({container_name: service}) - - return replace(state, after=project) - - -def update_service(state: State, update: dict[str, Any]) -> State: - project = copy.deepcopy(state.after) - service_name = state.module.params["name"] - - _ = _recursive_update(project["services"][service_name], update) - - return replace(state, after=project) - - -def remove_service(state: State) -> State: - project = copy.deepcopy(state.after) - service_name = state.module.params["name"] - - del project["services"][service_name] - - return replace(state, after=project) - - -def remove_labels(state: State, label_names: list[str]) -> State: - project = copy.deepcopy(state.after) - service_name = state.module.params["name"] - service = project["services"].get(service_name, {}) - - labels = service.get("labels", {}) - - if labels: - for label in labels: - if label in label_names: - try: - del service["labels"][label] - except KeyError: - pass - - service["labels"] = labels - - else: - try: - del service["labels"] - except KeyError: - pass - - project["services"][service_name] = service - - return replace(state, after=project) - - def update_project(state: State) -> State: + """Ensure that networks/volumes that exist in services also exist in the project.""" project = copy.deepcopy(state.after) project_services = project.get("services", {}) @@ -200,7 +108,7 @@ def update_project(state: State) -> State: service_volume_name: None for service_volume_name in service_volume_names if service_volume_name not in project_volumes - } + }, ) if service_network_names := service.get("networks").keys(): @@ -209,67 +117,37 @@ def update_project(state: State) -> State: service_network_name: None for service_network_name in service_network_names if service_network_name not in project_networks - } + }, ) return replace(state, after=project) def write_compose(state: State) -> State: + """Write the compose file to disk.""" file = state.compose_filepath - with open(file, mode="w") as stream: + with Path(file).open(mode="w") as stream: yaml.dump(state.after, stream) return state -def run_service( - extra_args: dict[str, Any] = {}, - helper: Callable[[State], State] = lambda _: _, +def run_module( + args: dict[str, Any] | None = None, + execute: Callable[[State], State] = lambda _: _, ) -> None: + """Handle module setup and teardown.""" module = AnsibleModule( - argument_spec={**BASE_SERVICE_ARGS, **extra_args}, + argument_spec={**BASE_ARGS, **(args or {})}, supports_check_mode=True, ) state = get_state(module) - if module.params["state"] == "absent": - state = remove_service(state) + state = execute(state) - else: - for f in [apply_service_base, set_service_defaults, helper, update_project]: - state = f(state) - - exit(state) - - -def run_label( - extra_args: dict[str, Any], helper: Callable[[State], State], label_names: list[str] -) -> None: - module = AnsibleModule( - argument_spec={**BASE_LABEL_ARGS, **extra_args}, - supports_check_mode=True, - ) - - state = get_state(module) - - if module.params["state"] == "absent": - state = remove_labels(state, label_names) - - else: - state = helper(state) - - exit(state) - - -def exit(state: State) -> None: - # TODO: Check diff and set changed variable - - if state.module.check_mode: - state.module.exit_json(**asdict(state.result)) # type: ignore[reportUnkownMemberType] - - _ = write_compose(state) + if not state.module.check_mode: + write_compose(state) state.module.exit_json(**asdict(state.result)) # type: ignore[reportUnkownMemberType] diff --git a/plugins/module_utils/label/__init__.py b/plugins/module_utils/label/__init__.py new file mode 100644 index 0000000..147baa1 --- /dev/null +++ b/plugins/module_utils/label/__init__.py @@ -0,0 +1,4 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""Module utilities for ez_compose label modules.""" diff --git a/plugins/module_utils/label/common.py b/plugins/module_utils/label/common.py new file mode 100644 index 0000000..a18da56 --- /dev/null +++ b/plugins/module_utils/label/common.py @@ -0,0 +1,21 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""Label module utilities.""" + +from __future__ import annotations + +from typing import Any, Callable + +from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( + State, + run_module, +) + + +def run( + extra_args: dict[str, Any] | None = None, + helper: Callable[[State], State] = lambda _: _, +) -> None: + """Wrap module execution function for label helpers.""" + run_module(extra_args or {}, helper) diff --git a/plugins/module_utils/label/docker_volume_backupper.py b/plugins/module_utils/label/docker_volume_backupper.py new file mode 100644 index 0000000..abc974c --- /dev/null +++ b/plugins/module_utils/label/docker_volume_backupper.py @@ -0,0 +1,37 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""Docker Volume Backup label helper.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label +import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service + +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State + + +def helper(state: State) -> State: + """Add labels to the service to stop it during backup.""" + stop = state.module.params.get("stop", True) + project_name = state.module.params["project_name"] + + update: dict[str, Any] = {} + + if stop: + update["labels"] = { + "docker-volume-backup.stop-during-backup": project_name, + } + + return service.update(state, update) + + +def run() -> None: + """Run the backupper label helper.""" + extra_args = { + "stop": {"type": "bool"}, + } + label.run(extra_args, helper) diff --git a/plugins/modules/label_traefik_middleware.py b/plugins/module_utils/label/traefik_middleware.py similarity index 70% rename from plugins/modules/label_traefik_middleware.py rename to plugins/module_utils/label/traefik_middleware.py index 99b17e3..34b5c87 100644 --- a/plugins/modules/label_traefik_middleware.py +++ b/plugins/module_utils/label/traefik_middleware.py @@ -1,27 +1,15 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections +from __future__ import annotations -DOCUMENTATION = r""" -""" +from typing import TYPE_CHECKING -EXAMPLES = r""" -""" +import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label +import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_label, - update_service, -) +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: @@ -43,7 +31,7 @@ def helper(state: State) -> State: "labels": labels, } - return update_service(state, update) + return service.update(state, update) def main(): @@ -53,7 +41,7 @@ def main(): "middleware": {"type": "str", "required": True}, "settings": {"type": "list", "required": True}, } - run_label(extra_args, helper) + label.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/modules/label_traefik_router.py b/plugins/module_utils/label/traefik_router.py similarity index 72% rename from plugins/modules/label_traefik_router.py rename to plugins/module_utils/label/traefik_router.py index 9272e80..7448ce0 100644 --- a/plugins/modules/label_traefik_router.py +++ b/plugins/module_utils/label/traefik_router.py @@ -1,34 +1,22 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections +from __future__ import annotations -DOCUMENTATION = r""" -""" +from typing import TYPE_CHECKING -EXAMPLES = r""" -""" +import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label +import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_label, - update_service, -) +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: service_name = state.module.params["name"] project_name = state.module.params["project_name"] rule = state.module.params["rule"] - service = state.module.params.get("service") + traefik_service = state.module.params.get("service") entrypoints = state.module.params.get("entrypoints") middlewares = state.module.params.get("middlewares") certresolver = state.module.params.get("certresolver") @@ -51,8 +39,8 @@ def helper(state: State) -> State: if entrypoints: labels[f"{prefix}.entrypoints"] = ",".join(entrypoints) - if service: - labels[f"{prefix}.service"] = service + if traefik_service: + labels[f"{prefix}.service"] = traefik_service if middlewares: labels[f"{prefix}.middlewares"] = ",".join(middlewares) @@ -61,7 +49,7 @@ def helper(state: State) -> State: "labels": labels, } - return update_service(state, update) + return service.update(state, update) def main(): @@ -76,7 +64,7 @@ def main(): "entrypoints": {"type": "list"}, "middlewares": {"type": "list"}, } - run_label(extra_args, helper) + label.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/modules/label_traefik_service.py b/plugins/module_utils/label/traefik_service.py similarity index 67% rename from plugins/modules/label_traefik_service.py rename to plugins/module_utils/label/traefik_service.py index 5de9af2..6e4b9c8 100644 --- a/plugins/modules/label_traefik_service.py +++ b/plugins/module_utils/label/traefik_service.py @@ -1,27 +1,15 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections +from __future__ import annotations -DOCUMENTATION = r""" -""" +from typing import TYPE_CHECKING -EXAMPLES = r""" -""" +import ansible_collections.snailed.ez_compose.plugins.module_utils.label.common as label +import ansible_collections.snailed.ez_compose.plugins.module_utils.service.common as service -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_label, - update_service, -) +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State from typing import Any @@ -48,7 +36,7 @@ def helper(state: State) -> State: "labels": labels, } - return update_service(state, update) + return service.update(state, update) def main(): @@ -57,7 +45,7 @@ def main(): "service_name": {"type": "string"}, "port": {"type": "int"}, } - run_label(extra_args, helper) + label.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/module_utils/service/__init__.py b/plugins/module_utils/service/__init__.py new file mode 100644 index 0000000..910034f --- /dev/null +++ b/plugins/module_utils/service/__init__.py @@ -0,0 +1,4 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""Module utilities for ez_compose service modules.""" diff --git a/plugins/module_utils/service/common.py b/plugins/module_utils/service/common.py new file mode 100644 index 0000000..17b3db4 --- /dev/null +++ b/plugins/module_utils/service/common.py @@ -0,0 +1,94 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +"""Service module utilities.""" + +from __future__ import annotations + +import copy +from dataclasses import replace +from typing import Any, Callable + +from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( + State, + recursive_update, + run_module, + update_project, +) + +BASE_SERVICE_ARGS = { + "project_name": { + "type": "str", + "required": True, + }, + "name": { + "type": "str", + "required": True, + }, + "image": { + "type": "str", + }, + "defaults": { + "type": "dict", + }, +} + + +def apply_base(state: State) -> State: + """Apply base service configuration.""" + service_name = state.module.params["name"] + project_name = state.module.params["project_name"] + image = state.module.params["image"] + + new: dict[str, Any] = { + "service_name": f"{project_name}_{service_name}", + "hostname": f"{project_name}_{service_name}", + "image": image, + "restart": "unless-stopped", + "environment": {}, + "labels": {}, + "volumes": [], + "networks": { + f"{project_name}_internal": None, + }, + } + + return update(state, new) + + +def set_defaults(state: State) -> State: + """Set default values for a service.""" + container_name = state.module.params["name"] + defaults = state.module.params["defaults"] + project = copy.deepcopy(state.after) + services = project["services"] + service = services[container_name] + + _ = recursive_update(service, defaults) + + services.update({container_name: service}) + + return replace(state, after=project) + + +def update(state: State, update: dict[str, Any]) -> State: + """Update a service with the given dictionary.""" + project = copy.deepcopy(state.after) + service_name = state.module.params["name"] + + _ = recursive_update(project["services"][service_name], update) + + return replace(state, after=project) + + +def run( + extra_args: dict[str, Any] | None = None, + helper: Callable[[State], State] = lambda _: _, +) -> None: + """Wrap module execution function for service helpers.""" + def execute(state: State) -> State: + for f in [apply_base, set_defaults, helper, update_project]: + state = f(state) + return state + + run_module({**BASE_SERVICE_ARGS, **(extra_args or {})}, execute) diff --git a/plugins/modules/service.py b/plugins/module_utils/service/custom.py similarity index 73% rename from plugins/modules/service.py rename to plugins/module_utils/service/custom.py index 048af58..89d706c 100644 --- a/plugins/modules/service.py +++ b/plugins/module_utils/service/custom.py @@ -1,34 +1,21 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections - -DOCUMENTATION = r""" -""" - -EXAMPLES = r""" -""" - -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] +from __future__ import annotations import copy +from typing import TYPE_CHECKING from ansible.module_utils.basic import AnsibleModule from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - exit, + finish, get_state, - remove_service, update_project, - update_service, ) +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: project_name = state.module.params["project_name"] @@ -44,7 +31,7 @@ def helper(state: State) -> State: update["networks"] = networks - return update_service(state, update) + return service.update(state, update) def main(): @@ -76,14 +63,10 @@ def main(): state = get_state(module) - if module.params["state"] == "absent": - state = remove_service(state) + for f in [helper, update_project]: + state = f(state) - else: - for f in [helper, update_project]: - state = f(state) - - exit(state) + finish(state) if __name__ == "__main__": diff --git a/plugins/modules/service_docker_in_docker.py b/plugins/module_utils/service/docker_in_docker.py similarity index 55% rename from plugins/modules/service_docker_in_docker.py rename to plugins/module_utils/service/docker_in_docker.py index f516cf1..00295e6 100644 --- a/plugins/modules/service_docker_in_docker.py +++ b/plugins/module_utils/service/docker_in_docker.py @@ -1,28 +1,14 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections +from __future__ import annotations -DOCUMENTATION = r""" -""" +from typing import TYPE_CHECKING -EXAMPLES = r""" -""" - -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_service, - update_service, -) +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: command = state.module.params.get("command") @@ -34,14 +20,14 @@ def helper(state: State) -> State: if command: update["command"] = command - return update_service(state, update) + return service.update(state, update) def main(): extra_args = { "command": {"type": "list"}, } - run_service(extra_args, helper) + service.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/modules/service_docker_socket_proxy.py b/plugins/module_utils/service/docker_socket_proxy.py similarity index 62% rename from plugins/modules/service_docker_socket_proxy.py rename to plugins/module_utils/service/docker_socket_proxy.py index 3161c81..665dcc4 100644 --- a/plugins/modules/service_docker_socket_proxy.py +++ b/plugins/module_utils/service/docker_socket_proxy.py @@ -1,27 +1,14 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections +from __future__ import annotations -DOCUMENTATION = r""" -""" +from typing import TYPE_CHECKING -EXAMPLES = r""" -""" +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_service, - update_service, -) +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: @@ -40,14 +27,14 @@ def helper(state: State) -> State: "volumes": volumes, } - return update_service(state, update) + return service.update(state, update) def main(): extra_args = { "read_only": {"type": "bool"}, } - run_service(extra_args, helper) + service.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/modules/service_docker_volume_backupper.py b/plugins/module_utils/service/docker_volume_backupper.py similarity index 80% rename from plugins/modules/service_docker_volume_backupper.py rename to plugins/module_utils/service/docker_volume_backupper.py index 309bafa..d7c17c0 100644 --- a/plugins/modules/service_docker_volume_backupper.py +++ b/plugins/module_utils/service/docker_volume_backupper.py @@ -1,27 +1,14 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections +from __future__ import annotations -DOCUMENTATION = r""" -""" +from typing import TYPE_CHECKING -EXAMPLES = r""" -""" +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_service, - update_service, -) +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: @@ -64,7 +51,7 @@ def helper(state: State) -> State: "volumes": volumes, } - return update_service(state, update) + return service.update(state, update) def main(): @@ -72,7 +59,7 @@ def main(): "archive": {"type": "str"}, "backup_volumes": {"type": "list"}, } - run_service(extra_args, helper) + service.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/modules/service_mariadb.py b/plugins/module_utils/service/mariadb.py similarity index 82% rename from plugins/modules/service_mariadb.py rename to plugins/module_utils/service/mariadb.py index 486e328..5576fb2 100644 --- a/plugins/modules/service_mariadb.py +++ b/plugins/module_utils/service/mariadb.py @@ -1,29 +1,15 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections - -DOCUMENTATION = r""" -""" - -EXAMPLES = r""" -""" - -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] +from __future__ import annotations import shlex +from typing import TYPE_CHECKING -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_service, - update_service, -) +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service + +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: @@ -40,7 +26,7 @@ def helper(state: State) -> State: "source": service_name, "target": "/var/lib/mysql", "type": "volume", - } + }, ] environment = { @@ -55,7 +41,7 @@ def helper(state: State) -> State: environment.update( { "MARIADB_ROOT_PASSWORD": root_password, - } + }, ) if backup: @@ -68,8 +54,8 @@ def helper(state: State) -> State: f"-p {shlex.quote(password)} " f">/backup/{shlex.quote(project_name)}.sql" "'" - ) - } + ), + }, ) # mysqldump -psecret --all-databases > /tmp/dumps/dump.sql volumes.append( @@ -77,7 +63,7 @@ def helper(state: State) -> State: "type": "volume", "source": f"{service_name}_backup", "target": "/backup", - } + }, ) update = { @@ -86,7 +72,7 @@ def helper(state: State) -> State: "labels": labels, } - return update_service(state, update) + return service.update(state, update) def main(): @@ -97,7 +83,7 @@ def main(): "password": {"type": "str", "required": True}, "root_password": {"type": "str"}, } - run_service(extra_args, helper) + service.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/modules/service_postgres.py b/plugins/module_utils/service/postgres.py similarity index 80% rename from plugins/modules/service_postgres.py rename to plugins/module_utils/service/postgres.py index 67da053..52fe21b 100644 --- a/plugins/modules/service_postgres.py +++ b/plugins/module_utils/service/postgres.py @@ -1,29 +1,15 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - # Copyright: (c) 2024, Luca Bilke # MIT License (see LICENSE) -# TODO: write ansible sections - -DOCUMENTATION = r""" -""" - -EXAMPLES = r""" -""" - -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] +from __future__ import annotations import shlex +from typing import TYPE_CHECKING -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_service, - update_service, -) +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service + +if TYPE_CHECKING: + from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State def helper(state: State) -> State: @@ -39,7 +25,7 @@ def helper(state: State) -> State: "source": service_name, "target": "/var/lib/postgresql/data", "type": "volume", - } + }, ] environment = { @@ -60,8 +46,8 @@ def helper(state: State) -> State: f"-U {shlex.quote(username)} " f"-f /backup/{shlex.quote(project_name)}.sql" "'" - ) - } + ), + }, ) volumes.append( @@ -69,7 +55,7 @@ def helper(state: State) -> State: "type": "volume", "source": f"{service_name}_backup", "target": "/backup", - } + }, ) update = { @@ -78,7 +64,7 @@ def helper(state: State) -> State: "labels": labels, } - return update_service(state, update) + return service.update(state, update) def main(): @@ -88,7 +74,7 @@ def main(): "username": {"type": "str", "required": True}, "password": {"type": "str", "required": True}, } - run_service(extra_args, helper) + service.run(extra_args, helper) if __name__ == "__main__": diff --git a/plugins/module_utils/service/redis.py b/plugins/module_utils/service/redis.py new file mode 100644 index 0000000..a6bf069 --- /dev/null +++ b/plugins/module_utils/service/redis.py @@ -0,0 +1,14 @@ +# Copyright: (c) 2024, Luca Bilke +# MIT License (see LICENSE) + +from __future__ import annotations + +from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service + + +def main(): + service.run() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/label_docker_volume_backupper.py b/plugins/modules/label_docker_volume_backupper.py deleted file mode 100644 index 63aed2b..0000000 --- a/plugins/modules/label_docker_volume_backupper.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2024, Luca Bilke -# MIT License (see LICENSE) - -# TODO: write ansible sections - -DOCUMENTATION = r""" -""" - -EXAMPLES = r""" -""" - -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from typing import Any - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( - State, - run_label, - update_service, -) - - -def helper(state: State) -> State: - stop = state.module.params.get("stop", True) - project_name = state.module.params["project_name"] - - update: dict[str, Any] = {} - - if stop: - update["labels"] = { - "docker-volume-backup.stop-during-backup": project_name, - } - - return update_service(state, update) - - -def main(): - extra_args = { - "stop": {"type": "bool"}, - } - run_label(extra_args, helper) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/service_redis.py b/plugins/modules/service_redis.py deleted file mode 100644 index a6da3bf..0000000 --- a/plugins/modules/service_redis.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2024, Luca Bilke -# MIT License (see LICENSE) - -# TODO: write ansible sections - -DOCUMENTATION = r""" -""" - -EXAMPLES = r""" -""" - -RETURN = r""" -""" - -from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues] - -from ansible_collections.snailed.ez_compose.plugins.module_utils.common import run_service - - -def main(): - run_service() - - -if __name__ == "__main__": - main() diff --git a/plugins/pyproject.toml b/plugins/pyproject.toml index b4d93b0..3124475 100644 --- a/plugins/pyproject.toml +++ b/plugins/pyproject.toml @@ -1,18 +1,27 @@ -[tool.black] +[tool.ruff] line-length = 100 +show-fixes = true +output-format = "grouped" + +[tool.ruff.lint] +select = ["ALL"] +ignore = ["ERA001", "TD002", "TD003"] + +[tool.ruff.format] +line-ending = "lf" + +[tool.ruff.lint.mccabe] +max-complexity = 10 + +[tool.ruff.lint.pydocstyle] +convention = "numpy" [tool.basedpyright] typeCheckingMode = "strict" -reportIgnoreCommentWithoutRule = true -reportUnusedCallResult = true -reportMissingTypeStubs = false -# handled by ruff -reportUnusedVariable = false +# Handled by ruff +reportIgnoreCommentWithoutRule = false reportUnusedImport = false -reportUndefinedVariable = false - - -[tool.ruff.lint] -# Irrelevant for ansible -ignore = ["E402", "F404"] +reportUnusedClass = false +reportUnusedFunction = false +reportUnusedVariable = false