#!/usr/bin/python # Copyright: (c) 2025, Luca Bilke <luca@bil.ke> # MIT License (see LICENSE) # ruff: noqa: E402 from __future__ import annotations DOCUMENTATION = """ --- module: compose version_added: 1.0.0 short_description: Simplify docker-compose deployments. description: Easily create docker-compose files using a single module seealso: - name: Compose file reference description: Complete reference of the compose file spec. link: https://docs.docker.com/reference/compose-file/ 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: external_networks: description: - Networks to mark as external. type: list elements: str external_volumes: description: - Volumes to mark as external. type: list elements: str 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 postgres 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: - Configuration for custom service. type: list elements: dict suboptions: name: description: - Name of the service type: str definition: description: - Service definition type: dict label_helpers: description: - Label helper configurations type: dict suboptions: docker_volume_backupper: description: - Configuration for docker_volume_backupper labels. type: list elements: dict suboptions: stop: description: - If true, stop the container when backing up. type: bool homepage: description: - Configuration for homepage labels. type: list elements: dict suboptions: name: description: - Name to set on dashboard. - Defaults to name of service. type: str href: description: - HREF to set on dashboard. type: str icon: description: - URL to an image to use as icon. type: str description: description: - Description text to set on dashboard. type: str group: description: - Group to place service under on dashboard. type: str widget: description: - Widget configuration. - See U(https://gethomepage.dev/configs/services/) for more info. type: dict traefik_middleware: description: - Configuration for traefik_middleware labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string middleware: description: - The name of the traefik middleware to use. type: str proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] settings: description: - Middleware options. type: dict traefik_router: description: - Configuration for traefik_router labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string rule: description: - Routing rule to match. type: str proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] 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: - Configuration for traefik_service labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] port: description: - Port to forward to. type: int protocol_version: description: - Proxy protocol version to use (TCP only). type: int docker_in_docker: description: - Configuration for docker_in_docker service. type: list elements: dict suboptions: name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict docker_socket_proxy: description: - Configuration for docker_socket_proxy service. type: list elements: dict suboptions: read_only: description: - If true, only allow read access to the docker socket. type: bool name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict docker_volume_backupper: description: - Configuration for docker_volume_backupper service. type: list elements: dict suboptions: archive: description: - Directory to store backups in. type: path backup_volumes: description: - List of volume names of volumes to backup. type: list elements: str docker_socket_proxy: description: - Hostname of a docker socket proxy to use. type: str name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict mariadb: description: - Configuration for mariadb service. type: list elements: dict suboptions: backup: description: - If true, add labels for the docker volume backupper. type: bool database: description: - Name of database. type: str username: description: - Username for database. type: str password: description: - Password for database. type: str root_password: description: - Root password for database. type: str name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict postgres: description: - Configuration for postgres service. type: list elements: dict suboptions: backup: description: - If true, add labels for the docker volume backupper. type: bool database: description: - Name of database. type: str username: description: - Username for database. type: str password: description: - Password for database. type: str name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict redis: description: - Configuration for redis service. type: list elements: dict suboptions: name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict label_default_args: description: - Default arguments for each label helper. type: dict suboptions: docker_volume_backupper: description: - Configuration for docker_volume_backupper labels. type: list elements: dict suboptions: stop: description: - If true, stop the container when backing up. type: bool homepage: description: - Configuration for homepage labels. type: list elements: dict suboptions: name: description: - Name to set on dashboard. - Defaults to name of service. type: str href: description: - HREF to set on dashboard. type: str icon: description: - URL to an image to use as icon. type: str description: description: - Description text to set on dashboard. type: str group: description: - Group to place service under on dashboard. type: str widget: description: - Widget configuration. - See U(https://gethomepage.dev/configs/services/) for more info. type: dict traefik_middleware: description: - Configuration for traefik_middleware labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string middleware: description: - The name of the traefik middleware to use. type: str proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] settings: description: - Middleware options. type: dict traefik_router: description: - Configuration for traefik_router labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string rule: description: - Routing rule to match. type: str proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] 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: - Configuration for traefik_service labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] port: description: - Port to forward to. type: int protocol_version: description: - Proxy protocol version to use (TCP only). type: int services: description: - Services to create in the project. type: dict suboptions: custom: description: - Configuration for custom service. type: list elements: dict suboptions: name: description: - Name of the service type: str required: true definition: description: - Service definition type: dict required: true label_helpers: description: - Label helper configurations type: dict suboptions: docker_volume_backupper: description: - Configuration for docker_volume_backupper labels. type: list elements: dict suboptions: stop: description: - If true, stop the container when backing up. type: bool homepage: description: - Configuration for homepage labels. type: list elements: dict suboptions: name: description: - Name to set on dashboard. - Defaults to name of service. type: str href: description: - HREF to set on dashboard. type: str icon: description: - URL to an image to use as icon. type: str description: description: - Description text to set on dashboard. type: str group: description: - Group to place service under on dashboard. type: str widget: description: - Widget configuration. - See U(https://gethomepage.dev/configs/services/) for more info. type: dict traefik_middleware: description: - Configuration for traefik_middleware labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string middleware: description: - The name of the traefik middleware to use. type: str proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] settings: description: - Middleware options. type: dict traefik_router: description: - Configuration for traefik_router labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string rule: description: - Routing rule to match. type: str proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] 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: - Configuration for traefik_service labels. type: list elements: dict suboptions: name: description: - Name of the middleware. type: string proxy_type: description: - Traefik proxy type. type: str choices: [http, tcp, udp] port: description: - Port to forward to. type: int protocol_version: description: - Proxy protocol version to use (TCP only). type: int docker_in_docker: description: - Configuration for docker_in_docker service. type: list elements: dict suboptions: name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict docker_socket_proxy: description: - Configuration for docker_socket_proxy service. type: list elements: dict suboptions: read_only: description: - If true, only allow read access to the docker socket. type: bool required: true name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict docker_volume_backupper: description: - Configuration for docker_volume_backupper service. type: list elements: dict suboptions: archive: description: - Directory to store backups in. type: path backup_volumes: description: - List of volume names of volumes to backup. type: list elements: str required: true docker_socket_proxy: description: - Hostname of a docker socket proxy to use. type: str name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict mariadb: description: - Configuration for mariadb service. type: list elements: dict suboptions: backup: description: - If true, add labels for the docker volume backupper. type: bool default: true 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 name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict postgres: description: - Configuration for postgres service. type: list elements: dict suboptions: backup: description: - If true, add labels for the docker volume backupper. type: bool default: true 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 name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict redis: description: - Configuration for redis service. type: list elements: dict suboptions: name: description: - Name of the service. type: str overwrite: description: - Definition to force. type: dict """ # noqa: E501 from dataclasses import asdict from importlib.util import find_spec from ansible.module_utils.basic import AnsibleModule # pyright: ignore[reportMissingTypeStubs] from ansible_collections.snailed.ez_docker.plugins.module_utils import ( common, service, spec, ) def main() -> None: module = AnsibleModule( argument_spec={ "name": { "aliases": ["project"], "type": "str", "required": True, }, "project_dir": { "type": "path", "default": "/var/lib/ez_docker", }, "settings": spec.settings_spec(), "services": spec.service_argument_spec(), }, supports_check_mode=True, ) if not find_spec("yaml"): module.fail_json("PyYAML needs to be installed on the host to use this plugin") # pyright: ignore[reportUnknownMemberType] try: state = common.get_state(module) except Exception as e: # noqa: BLE001 module.fail_json(f"Error while reading existing compose file: {e}") # pyright: ignore[reportUnknownMemberType] for name, services_params in state.params.get("services", {}).items(): for index, service_params in enumerate(services_params): default_params = service.common.get_default_args(state, name) params = common.recursive_update(default_params, service_params) params["_index"] = index helper = getattr(service, name).helper state = service.common.run_helper(state, params, helper) state = common.update_project(state) state = common.set_result(state) if state.result.changed and not module.check_mode: try: state = common.write_compose(state) except Exception as e: # noqa: BLE001 module.fail_json(f"Error while writing new compose file: {e}") # pyright: ignore[reportUnknownMemberType] ret = asdict(state.result) module.exit_json(**ret) # pyright: ignore[reportUnknownMemberType] if __name__ == "__main__": main()