This commit is contained in:
Luca Bilke 2024-12-11 18:42:59 +01:00
parent 2006d16b30
commit dd82a95cf1
Signed by: luca
GPG key ID: F6E11C9BAA7C82F5
21 changed files with 325 additions and 456 deletions

4
plugins/__init__.py Normal file
View file

@ -0,0 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""A collection to ease the creation of docker compose files using ansible."""

View file

@ -0,0 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Module utilities for ez_compose modules."""

View file

@ -1,17 +1,21 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # MIT License (see LICENSE)
"""Common module utilities."""
from __future__ import annotations from __future__ import annotations
import copy import copy
from dataclasses import asdict, dataclass, field, replace from dataclasses import asdict, dataclass, field, replace
from pathlib import Path
from typing import Any, Callable from typing import Any, Callable
import yaml import yaml
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
PROJECTS_DIR = "/var/lib/ez_compose" PROJECTS_DIR = "/var/lib/ez_compose"
BASE_SERVICE_ARGS = {
BASE_ARGS = {
"project_name": { "project_name": {
"type": "str", "type": "str",
"required": True, "required": True,
@ -20,59 +24,41 @@ BASE_SERVICE_ARGS = {
"type": "str", "type": "str",
"required": True, "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) @dataclass(frozen=True)
class Result: class Result:
"""Module result object."""
changed: bool = False changed: bool = False
diff: dict[str, Any] = field(default_factory=dict) diff: dict[str, Any] = field(default_factory=dict)
@dataclass(frozen=True) # @dataclass(frozen=True)
class Settings: # class Settings:
projects_dir: str = "/usr/local/share/ez_compose/" # projects_dir: str = "/usr/local/share/ez_compose/"
@dataclass(frozen=True) @dataclass(frozen=True)
class State: class State:
module: Any # Replace Any with the actual type of AnsibleModule if available """Execution state object."""
module: AnsibleModule
result: Result result: Result
compose_filepath: str compose_filepath: str
before: dict[str, Any] before: dict[str, Any]
after: dict[str, Any] after: dict[str, Any]
def _recursive_update(default: dict[Any, Any], update: dict[Any, Any]) -> dict[Any, Any]: def recursive_update(
for key in 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): 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): elif isinstance(update[key], list) and isinstance(default.get(key), list):
default_set = set(default[key]) 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: 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: try:
with open(compose_filepath, "r") as fp: with Path(compose_filepath).open("r") as fp:
before = yaml.safe_load(fp) before = yaml.safe_load(fp)
except FileNotFoundError: except FileNotFoundError:
@ -100,92 +89,11 @@ def get_state(module: AnsibleModule) -> State:
result=Result(), result=Result(),
compose_filepath=compose_filepath, compose_filepath=compose_filepath,
before=before, 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: 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 = copy.deepcopy(state.after)
project_services = project.get("services", {}) project_services = project.get("services", {})
@ -200,7 +108,7 @@ def update_project(state: State) -> State:
service_volume_name: None service_volume_name: None
for service_volume_name in service_volume_names for service_volume_name in service_volume_names
if service_volume_name not in project_volumes if service_volume_name not in project_volumes
} },
) )
if service_network_names := service.get("networks").keys(): if service_network_names := service.get("networks").keys():
@ -209,67 +117,37 @@ def update_project(state: State) -> State:
service_network_name: None service_network_name: None
for service_network_name in service_network_names for service_network_name in service_network_names
if service_network_name not in project_networks if service_network_name not in project_networks
} },
) )
return replace(state, after=project) return replace(state, after=project)
def write_compose(state: State) -> State: def write_compose(state: State) -> State:
"""Write the compose file to disk."""
file = state.compose_filepath file = state.compose_filepath
with open(file, mode="w") as stream: with Path(file).open(mode="w") as stream:
yaml.dump(state.after, stream) yaml.dump(state.after, stream)
return state return state
def run_service( def run_module(
extra_args: dict[str, Any] = {}, args: dict[str, Any] | None = None,
helper: Callable[[State], State] = lambda _: _, execute: Callable[[State], State] = lambda _: _,
) -> None: ) -> None:
"""Handle module setup and teardown."""
module = AnsibleModule( module = AnsibleModule(
argument_spec={**BASE_SERVICE_ARGS, **extra_args}, argument_spec={**BASE_ARGS, **(args or {})},
supports_check_mode=True, supports_check_mode=True,
) )
state = get_state(module) state = get_state(module)
if module.params["state"] == "absent": state = execute(state)
state = remove_service(state)
else: if not state.module.check_mode:
for f in [apply_service_base, set_service_defaults, helper, update_project]: write_compose(state)
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)
state.module.exit_json(**asdict(state.result)) # type: ignore[reportUnkownMemberType] state.module.exit_json(**asdict(state.result)) # type: ignore[reportUnkownMemberType]

View file

@ -0,0 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Module utilities for ez_compose label modules."""

View file

@ -0,0 +1,21 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# 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)

View file

@ -0,0 +1,37 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# 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)

View file

@ -1,27 +1,15 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # 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""" if TYPE_CHECKING:
""" from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
run_label,
update_service,
)
def helper(state: State) -> State: def helper(state: State) -> State:
@ -43,7 +31,7 @@ def helper(state: State) -> State:
"labels": labels, "labels": labels,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -53,7 +41,7 @@ def main():
"middleware": {"type": "str", "required": True}, "middleware": {"type": "str", "required": True},
"settings": {"type": "list", "required": True}, "settings": {"type": "list", "required": True},
} }
run_label(extra_args, helper) label.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,34 +1,22 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # 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""" if TYPE_CHECKING:
""" from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
run_label,
update_service,
)
def helper(state: State) -> State: def helper(state: State) -> State:
service_name = state.module.params["name"] service_name = state.module.params["name"]
project_name = state.module.params["project_name"] project_name = state.module.params["project_name"]
rule = state.module.params["rule"] rule = state.module.params["rule"]
service = state.module.params.get("service") traefik_service = state.module.params.get("service")
entrypoints = state.module.params.get("entrypoints") entrypoints = state.module.params.get("entrypoints")
middlewares = state.module.params.get("middlewares") middlewares = state.module.params.get("middlewares")
certresolver = state.module.params.get("certresolver") certresolver = state.module.params.get("certresolver")
@ -51,8 +39,8 @@ def helper(state: State) -> State:
if entrypoints: if entrypoints:
labels[f"{prefix}.entrypoints"] = ",".join(entrypoints) labels[f"{prefix}.entrypoints"] = ",".join(entrypoints)
if service: if traefik_service:
labels[f"{prefix}.service"] = service labels[f"{prefix}.service"] = traefik_service
if middlewares: if middlewares:
labels[f"{prefix}.middlewares"] = ",".join(middlewares) labels[f"{prefix}.middlewares"] = ",".join(middlewares)
@ -61,7 +49,7 @@ def helper(state: State) -> State:
"labels": labels, "labels": labels,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -76,7 +64,7 @@ def main():
"entrypoints": {"type": "list"}, "entrypoints": {"type": "list"},
"middlewares": {"type": "list"}, "middlewares": {"type": "list"},
} }
run_label(extra_args, helper) label.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,27 +1,15 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # 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""" if TYPE_CHECKING:
""" from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
run_label,
update_service,
)
from typing import Any from typing import Any
@ -48,7 +36,7 @@ def helper(state: State) -> State:
"labels": labels, "labels": labels,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -57,7 +45,7 @@ def main():
"service_name": {"type": "string"}, "service_name": {"type": "string"},
"port": {"type": "int"}, "port": {"type": "int"},
} }
run_label(extra_args, helper) label.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -0,0 +1,4 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE)
"""Module utilities for ez_compose service modules."""

View file

@ -0,0 +1,94 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# 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)

View file

@ -1,34 +1,21 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # MIT License (see LICENSE)
# TODO: write ansible sections from __future__ import annotations
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
"""
RETURN = r"""
"""
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
import copy import copy
from typing import TYPE_CHECKING
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State, finish,
exit,
get_state, get_state,
remove_service,
update_project, 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: def helper(state: State) -> State:
project_name = state.module.params["project_name"] project_name = state.module.params["project_name"]
@ -44,7 +31,7 @@ def helper(state: State) -> State:
update["networks"] = networks update["networks"] = networks
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -76,14 +63,10 @@ def main():
state = get_state(module) state = get_state(module)
if module.params["state"] == "absent": for f in [helper, update_project]:
state = remove_service(state) state = f(state)
else: finish(state)
for f in [helper, update_project]:
state = f(state)
exit(state)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,28 +1,14 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # 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: def helper(state: State) -> State:
command = state.module.params.get("command") command = state.module.params.get("command")
@ -34,14 +20,14 @@ def helper(state: State) -> State:
if command: if command:
update["command"] = command update["command"] = command
return update_service(state, update) return service.update(state, update)
def main(): def main():
extra_args = { extra_args = {
"command": {"type": "list"}, "command": {"type": "list"},
} }
run_service(extra_args, helper) service.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,27 +1,14 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # 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""" if TYPE_CHECKING:
""" from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
run_service,
update_service,
)
def helper(state: State) -> State: def helper(state: State) -> State:
@ -40,14 +27,14 @@ def helper(state: State) -> State:
"volumes": volumes, "volumes": volumes,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
extra_args = { extra_args = {
"read_only": {"type": "bool"}, "read_only": {"type": "bool"},
} }
run_service(extra_args, helper) service.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,27 +1,14 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # 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""" if TYPE_CHECKING:
""" from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import (
State,
run_service,
update_service,
)
def helper(state: State) -> State: def helper(state: State) -> State:
@ -64,7 +51,7 @@ def helper(state: State) -> State:
"volumes": volumes, "volumes": volumes,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -72,7 +59,7 @@ def main():
"archive": {"type": "str"}, "archive": {"type": "str"},
"backup_volumes": {"type": "list"}, "backup_volumes": {"type": "list"},
} }
run_service(extra_args, helper) service.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,29 +1,15 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # MIT License (see LICENSE)
# TODO: write ansible sections from __future__ import annotations
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
"""
RETURN = r"""
"""
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
import shlex import shlex
from typing import TYPE_CHECKING
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
State,
run_service, if TYPE_CHECKING:
update_service, from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
)
def helper(state: State) -> State: def helper(state: State) -> State:
@ -40,7 +26,7 @@ def helper(state: State) -> State:
"source": service_name, "source": service_name,
"target": "/var/lib/mysql", "target": "/var/lib/mysql",
"type": "volume", "type": "volume",
} },
] ]
environment = { environment = {
@ -55,7 +41,7 @@ def helper(state: State) -> State:
environment.update( environment.update(
{ {
"MARIADB_ROOT_PASSWORD": root_password, "MARIADB_ROOT_PASSWORD": root_password,
} },
) )
if backup: if backup:
@ -68,8 +54,8 @@ def helper(state: State) -> State:
f"-p {shlex.quote(password)} " f"-p {shlex.quote(password)} "
f">/backup/{shlex.quote(project_name)}.sql" f">/backup/{shlex.quote(project_name)}.sql"
"'" "'"
) ),
} },
) )
# mysqldump -psecret --all-databases > /tmp/dumps/dump.sql # mysqldump -psecret --all-databases > /tmp/dumps/dump.sql
volumes.append( volumes.append(
@ -77,7 +63,7 @@ def helper(state: State) -> State:
"type": "volume", "type": "volume",
"source": f"{service_name}_backup", "source": f"{service_name}_backup",
"target": "/backup", "target": "/backup",
} },
) )
update = { update = {
@ -86,7 +72,7 @@ def helper(state: State) -> State:
"labels": labels, "labels": labels,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -97,7 +83,7 @@ def main():
"password": {"type": "str", "required": True}, "password": {"type": "str", "required": True},
"root_password": {"type": "str"}, "root_password": {"type": "str"},
} }
run_service(extra_args, helper) service.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,29 +1,15 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# MIT License (see LICENSE) # MIT License (see LICENSE)
# TODO: write ansible sections from __future__ import annotations
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
"""
RETURN = r"""
"""
from __future__ import annotations # pyright: ignore[reportGeneralTypeIssues]
import shlex import shlex
from typing import TYPE_CHECKING
from ansible_collections.snailed.ez_compose.plugins.module_utils.common import ( from ansible_collections.snailed.ez_compose.plugins.module_utils.service import common as service
State,
run_service, if TYPE_CHECKING:
update_service, from ansible_collections.snailed.ez_compose.plugins.module_utils.common import State
)
def helper(state: State) -> State: def helper(state: State) -> State:
@ -39,7 +25,7 @@ def helper(state: State) -> State:
"source": service_name, "source": service_name,
"target": "/var/lib/postgresql/data", "target": "/var/lib/postgresql/data",
"type": "volume", "type": "volume",
} },
] ]
environment = { environment = {
@ -60,8 +46,8 @@ def helper(state: State) -> State:
f"-U {shlex.quote(username)} " f"-U {shlex.quote(username)} "
f"-f /backup/{shlex.quote(project_name)}.sql" f"-f /backup/{shlex.quote(project_name)}.sql"
"'" "'"
) ),
} },
) )
volumes.append( volumes.append(
@ -69,7 +55,7 @@ def helper(state: State) -> State:
"type": "volume", "type": "volume",
"source": f"{service_name}_backup", "source": f"{service_name}_backup",
"target": "/backup", "target": "/backup",
} },
) )
update = { update = {
@ -78,7 +64,7 @@ def helper(state: State) -> State:
"labels": labels, "labels": labels,
} }
return update_service(state, update) return service.update(state, update)
def main(): def main():
@ -88,7 +74,7 @@ def main():
"username": {"type": "str", "required": True}, "username": {"type": "str", "required": True},
"password": {"type": "str", "required": True}, "password": {"type": "str", "required": True},
} }
run_service(extra_args, helper) service.run(extra_args, helper)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -0,0 +1,14 @@
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# 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()

View file

@ -1,51 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# 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()

View file

@ -1,28 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2024, Luca Bilke <luca@bil.ke>
# 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()

View file

@ -1,18 +1,27 @@
[tool.black] [tool.ruff]
line-length = 100 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] [tool.basedpyright]
typeCheckingMode = "strict" typeCheckingMode = "strict"
reportIgnoreCommentWithoutRule = true
reportUnusedCallResult = true
reportMissingTypeStubs = false
# handled by ruff # Handled by ruff
reportUnusedVariable = false reportIgnoreCommentWithoutRule = false
reportUnusedImport = false reportUnusedImport = false
reportUndefinedVariable = false reportUnusedClass = false
reportUnusedFunction = false
reportUnusedVariable = false
[tool.ruff.lint]
# Irrelevant for ansible
ignore = ["E402", "F404"]