#!/usr/bin/env python # Copyright: (c) 2024, Luca Bilke <luca@bil.ke> # MIT License (see LICENSE) # ruff: noqa: E402 from __future__ import annotations from copy import deepcopy DOCUMENTATION = """ --- module: compose version_added: 1.0.0 short_description: Simplify docker-compose deployments seealso: - name: Compose file reference description: Complete reference of the compose file spec. link: https://docs.docker.com/reference/compose-file/ description: - Easily create docker-compose files using a single module author: - "Luca Bilke (@ssnailed)" attributes: check_mode: support: full diff_mode: support: full options: name: description: - Name of the compose project to create or modify. aliases: [project] type: str project_dir: description: - Path to store project directory under. type: path settings: description: - Settings/Defaults for the module. type: dict suboptions: default_definition: description: - Default definition for all containers. - Overwritten by per-service defaults. type: dict service_default_definitions: description: - Default definitions for each service type: dict suboptions: custom: description: - Default definitions for custom services. type: dict docker_in_docker: description: - Default definitions for docker in docker services. type: dict docker_socket_proxy: description: - Default definitions for docker socket proxy services. type: dict docker_volume_backupper: description: - Default definitions for docker volume backupper services. type: dict mariadb: description: - Default definitions for MariaDB services. type: dict postgres: description: - Default definitions for PostgreSQL services. type: dict redis: description: - Default definitions for Redis services. type: dict service_default_args: description: - Default arguments for each service helper type: dict suboptions: custom: description: - Default args for custom services. type: dict docker_in_docker: description: - Default args for docker in docker services. type: dict docker_socket_proxy: description: - Default args for docker socket proxy services. type: dict docker_volume_backupper: description: - Default args for docker volume backupper services. type: dict mariadb: description: - Default args for MariaDB services. type: dict postgres: description: - Default args for PostgreSQL services. type: dict redis: description: - Default args for Redis services. type: dict services: description: - Services to create in the project. type: dict suboptions: custom: description: - Custom service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict internal_network: description: - If true, add internal network to service. type: bool default: false label_helpers: description: - Label helper configurations. type: dict suboptions: docker_volume_backupper: description: - Docker Volume Backupper label helper configuration. type: dict suboptions: stop: description: - If true, stop the container when backing up. type: bool default: true traefik_middleware: description: - Traefik Middleware label helper configuration. type: dict suboptions: proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] default: http name: description: - Name of the middleware. type: string middleware: description: - The traefik middleware to use. type: str required: true settings: description: - Middleware options. type: dict required: true traefik_router: description: - Traefik Router label helper configuration. type: dict suboptions: proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] default: http name: description: - Name of the middleware. type: string rule: description: - Routing rule to match. type: str required: true service: description: - Traefik service to point at. type: str certresolver: description: - Certresolver to use. type: str entrypoints: description: - Entrypoints to listen on. type: list elements: str middlewares: description: - Middlewares to use. type: list elements: str traefik_service: description: - Traefik Service label helper configuration. type: dict suboptions: proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] default: http name: description: - Name of the middleware. type: string port: description: - Port to forward to. type: int docker_in_docker: description: - Docker-in-Docker service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict docker_socket_proxy: description: - Docker Socket Proxy service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict read_only: description: - If true, only allow read access to the docker socket. type: bool default: true docker_volume_backupper: description: - Docker Socket Proxy service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict archive: description: - Directory to store backups in. type: path backup_volumes: description: - List of volume names of volumes to backup. type: list elements: str mariadb: description: - MariaDB service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict backup: description: - If true, add labels for the docker volume backupper. type: bool database: description: - Name of database. type: str required: true username: description: - Username for database. type: str required: true password: description: - Password for database. type: str required: true root_password: description: - Root password for database. type: str postgres: description: - PostgreSQL service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict backup: description: - If true, add labels for the docker volume backupper. type: bool database: description: - Name of database. type: str required: true username: description: - Username for database. type: str required: true password: description: - Password for database. type: str required: true redis: description: - Redis service definition. type: dict suboptions: name: description: - Name of the service. type: str image: description: - Image to use for service. type: str defaults: description: - Service definition to be overwritten. type: dict overwrite: description: - Service definition to overwrite with. type: dict """ from typing import Any from ansible.module_utils.basic import AnsibleModule # type: ignore[reportMissingStubFile] from ansible_collections.snailed.ez_compose.plugins.module_utils import ( common, label, service, ) def label_argument_spec() -> dict[str, Any]: label_args: dict[str, Any] = { "type": "dict", "options": {}, } for module in label.__all__: if module == "common": continue options = { **label.common.BASE_ARGS, **getattr(label, module).EXTRA_ARGS, } label_args["options"][module] = { "type": "dict", "options": options, } return label_args def service_argument_spec() -> dict[str, Any]: service_args: dict[str, Any] = { "type": "dict", "suboptions": {}, "required": True, } for module in service.__all__: if module == "common": continue options = { **service.common.BASE_ARGS, **getattr(service, module).EXTRA_ARGS, } # TODO: move to service.common if module == "custom": options["label_helpers"] = label_argument_spec() service_args["suboptions"][module] = { "type": "dict", "suboptions": options, } return service_args def settings_spec() -> dict[str, Any]: settings: dict[str, Any] = { "type": "dict", "suboptions": { "default_definition": {"type": "dict"}, "service_default_args": {"type": "dict", "suboptions": {}}, "service_default_definitions": {"type": "dict", "suboptions": {}}, }, } for module in service.__all__: if module == "common": continue settings["suboptions"]["service_default_definitions"]["suboptions"][module] = { "type": "dict", } args = deepcopy(getattr(service, module).EXTRA_ARGS) for arg in args.values(): arg.pop("required", None) settings["suboptions"]["service_default_args"]["suboptions"][module] = { "type": "dict", "suboptions": args, } return settings def main() -> None: module = AnsibleModule( argument_spec={ "name": { "aliases": ["project"], "type": "str", "required": True, }, "project_dir": { "type": "path", "default": "/var/lib/ez_compose", }, "settings": settings_spec(), "services": service_argument_spec(), }, ) state = common.get_state(module) for name, args in module.params["services"].items(): state = getattr(service, name).helper(state, args) if __name__ == "__main__": main()