From 2d981027635cc7c05344db473c96e34beb3e965e Mon Sep 17 00:00:00 2001 From: s3lph <s3lph@kabelsalat.ch> Date: Wed, 5 Mar 2025 22:12:21 +0100 Subject: [PATCH] feat: initial commit --- .ansible-lint | 12 ++ .forgejo/workflows/aardoc.patch | 153 ++++++++++++++ .forgejo/workflows/ansible-galaxy.yml | 45 ++++ .forgejo/workflows/ansible-lint.yml | 35 ++++ .gitignore | 2 + .yamllint | 9 + README.md | 3 + galaxy.yml | 70 +++++++ meta/runtime.yml | 52 +++++ roles/pretalx/handlers/main.yml | 7 + roles/pretalx/meta/argument_specs.yml | 181 ++++++++++++++++ roles/pretalx/meta/main.yml | 42 ++++ roles/pretalx/tasks/install.yml | 110 ++++++++++ roles/pretalx/tasks/main.yml | 7 + .../templates/etc/pretalx/pretalx.cfg.j2 | 47 +++++ roles/pretix/handlers/main.yml | 7 + roles/pretix/meta/argument_specs.yml | 198 ++++++++++++++++++ roles/pretix/meta/main.yml | 42 ++++ roles/pretix/tasks/install.yml | 119 +++++++++++ roles/pretix/tasks/main.yml | 7 + roles/pretix/templates/etc/cron.d/pretix.j2 | 3 + .../pretix/templates/etc/pretix/pretix.cfg.j2 | 47 +++++ roles/watchtower/meta/argument_specs.yml | 42 ++++ roles/watchtower/meta/main.yml | 42 ++++ roles/watchtower/tasks/install.yml | 25 +++ roles/watchtower/tasks/main.yml | 7 + 26 files changed, 1314 insertions(+) create mode 100644 .ansible-lint create mode 100644 .forgejo/workflows/aardoc.patch create mode 100644 .forgejo/workflows/ansible-galaxy.yml create mode 100644 .forgejo/workflows/ansible-lint.yml create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 README.md create mode 100644 galaxy.yml create mode 100644 meta/runtime.yml create mode 100644 roles/pretalx/handlers/main.yml create mode 100644 roles/pretalx/meta/argument_specs.yml create mode 100644 roles/pretalx/meta/main.yml create mode 100644 roles/pretalx/tasks/install.yml create mode 100644 roles/pretalx/tasks/main.yml create mode 100644 roles/pretalx/templates/etc/pretalx/pretalx.cfg.j2 create mode 100644 roles/pretix/handlers/main.yml create mode 100644 roles/pretix/meta/argument_specs.yml create mode 100644 roles/pretix/meta/main.yml create mode 100644 roles/pretix/tasks/install.yml create mode 100644 roles/pretix/tasks/main.yml create mode 100644 roles/pretix/templates/etc/cron.d/pretix.j2 create mode 100644 roles/pretix/templates/etc/pretix/pretix.cfg.j2 create mode 100644 roles/watchtower/meta/argument_specs.yml create mode 100644 roles/watchtower/meta/main.yml create mode 100644 roles/watchtower/tasks/install.yml create mode 100644 roles/watchtower/tasks/main.yml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..4bdfa07 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,12 @@ +--- + +skip_list: + - meta-runtime[unsupported-version] + - galaxy[no-changelog] + - galaxy[version-incorrect] + - name[casing] + - var-naming[no-role-prefix] + +# Exclude defaults files generated by aar-doc +exclude_paths: + - "**/defaults/" diff --git a/.forgejo/workflows/aardoc.patch b/.forgejo/workflows/aardoc.patch new file mode 100644 index 0000000..25e5893 --- /dev/null +++ b/.forgejo/workflows/aardoc.patch @@ -0,0 +1,153 @@ +--- aar_doc/defaults.py ++++ aar_doc/defaults.py +@@ -15,7 +15,7 @@ + from ruamel.yaml.scalarstring import LiteralScalarString, SingleQuotedScalarString + + yaml = YAML() +-yaml.indent(mapping=2, sequence=2, offset=2) ++yaml.indent(mapping=2, sequence=4, offset=2) + yaml.encoding = "utf-8" + yaml.allow_unicode = True + +--- aar_doc/defaults.py ++++ aar_doc/defaults.py +@@ -73,18 +73,27 @@ def add_default( + else: + self._defaults.setdefault(name, RoleDefault(name, value, description)) + ++ def safe_quote_recursive(self, value): ++ if isinstance(value, list): ++ return [self.safe_quote_recursive(v) for v in value] ++ elif isinstance(value, dict): ++ return {k: self.safe_quote_recursive(v) for k, v in value.items()} ++ elif isinstance(value, str): ++ if value in ("yes", "no"): ++ return SingleQuotedScalarString(value) ++ elif "\n" in value: ++ return LiteralScalarString(value) ++ elif ":" in value: ++ return SingleQuotedScalarString(value) ++ return value ++ + def to_commented_map(self) -> CommentedMap: + """ + Returns all tracked defaults as a CommentedMap. + """ + commented_defaults = CommentedMap() + for role_default in self.defaults: +- value = role_default.value +- if isinstance(value, str): +- if value in ("yes", "no"): +- value = SingleQuotedScalarString(value) +- if "\n" in value: +- value = LiteralScalarString(value) ++ value = self.safe_quote_recursive(role_default.value) + commented_defaults[role_default.name] = value + description_items = ( + role_default.description +--- aar_doc/core.py ++++ aar_doc/core.py +@@ -7,6 +7,7 @@ and rendering jinja2 templates from processing data. + + import json + import pathlib ++import re + from enum import Enum + + import jinja2 +@@ -21,6 +22,24 @@ yaml.encoding = "utf-8" + yaml.allow_unicode = True + + ++def ansible_doc_markup(text): ++ # Regular expressions copied from ansible-doc: ++ # https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/doc.py#L436 ++ out = re.sub(r'\bI\(([^)]+)\)', r'*\1*', text) # I(text) -> *text* ++ out = re.sub(r'\bB\(([^)]+)\)', r'**\1**', out) # B(text) -> **text** ++ out = re.sub(r'\bC\(([^)]+)\)', r'`\1`', out) # C(text) -> `text` ++ out = re.sub(r'\bM\(([^)]+)\)', r'`\1`', out) # M(module) -> `module` ++ out = re.sub(r'\bO\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # O(option) -> `option` ++ out = re.sub(r'\bV\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # V(value) -> `value` ++ out = re.sub(r'\bV\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # E(env) -> `env` ++ out = re.sub(r'\bV\(((?:[^\\)]+|\\.)+)\)', r'`\1`', out) # RV(retval) -> `retval` ++ out = re.sub(r'\bU\(([^)]+)\)', r'[\1]', out) # U(url) -> [url] ++ out = re.sub(r'\bL\(([^)]+), *([^)]+)\)', r'[\1](\2)', out) # L(text,url) -> [text](url) ++ out = re.sub(r'\bR\(([^)]+), *([^)]+)\)', r'[\1](#\2)', out) # R(text,frag) -> [text](#frag) ++ out = re.sub(r'\bHORIZONTALLINE\b', r'\n\n---\n', out) # HORIZONTALLINE -> --- ++ return out ++ ++ + class OutputMode(Enum): + """ + Defines the options for the output mode. +@@ -240,6 +259,7 @@ def render_content(ctx: typer.Context, content_template: str) -> str: + autoescape=jinja2.select_autoescape(), + undefined=jinja2.StrictUndefined, + ) ++ env.filters['ansible_doc_markup'] = ansible_doc_markup + + role = ctx.obj["config"]["role"] + metadata = ctx.obj["data"]["metadata"] +@@ -270,12 +290,14 @@ def render_content(ctx: typer.Context, content_template: str) -> str: + keep_trailing_newline=True, + loader=jinja2.FileSystemLoader([role_path, output_template_file.parent]), + ) ++ env.filters['ansible_doc_markup'] = ansible_doc_markup + template = env.get_template(output_template_file.name) + except (FileNotFoundError, OSError): + env = jinja2.Environment( + keep_trailing_newline=True, + loader=jinja2.FileSystemLoader(role_path), + ) ++ env.filters['ansible_doc_markup'] = ansible_doc_markup + template = env.from_string(source=output_template) + + return template.render( +--- aar_doc/templates/markdown.j2 ++++ aar_doc/templates/markdown.j2 +@@ -3,7 +3,7 @@ + {%- if "version" in galaxy_collection %} + Version: {{ galaxy_collection.version }} + {% endif %} +-{{ metadata.galaxy_info.description }} ++{{ metadata.galaxy_info.description | ansible_doc_markup }} + {% if ("galaxy_tags" in metadata.galaxy_info) and (metadata.galaxy_info.galaxy_tags | length > 0) %} + Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} + {%- endif %} +@@ -22,14 +22,15 @@ Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} + + ### Entrypoint: {{ entrypoint }} + +-{{ argument_specs[entrypoint].short_description }} ++{{ argument_specs[entrypoint].short_description | ansible_doc_markup }} + + {% if "description" in argument_specs[entrypoint] %} + {%- if argument_specs[entrypoint].description is string -%} +-{{ argument_specs[entrypoint].description }} ++{{ argument_specs[entrypoint].description | ansible_doc_markup }} + {% else %} + {%- for line in argument_specs[entrypoint].description -%} +-{{ line }} ++{{ line | ansible_doc_markup }} ++ + {% endfor -%} + {% endif -%} + {% endif -%} +@@ -39,7 +40,7 @@ Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} + |Option|Description|Type|Required|Default| + |---|---|---|---|---| + {%- for name, details in options.items() %} +-| {{ name }} | {{ details.display_description }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | ++| {{ name }} | {{ details.display_description | ansible_doc_markup }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | + {%- endfor %} + + {% if entrypoint_options[entrypoint] | length > 1 -%} +@@ -49,7 +50,7 @@ Tags: {{ metadata.galaxy_info.galaxy_tags | join(', ') }} + |Option|Description|Type|Required|Default| + |---|---|---|---|---| + {%- for name, details in options.items() %} +-| {{ name }} | {{ details.display_description }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | ++| {{ name }} | {{ details.display_description | ansible_doc_markup }} | {{ details.display_type }} | {{ details.display_required }} | {{ details.display_default }} | + {%- endfor %} + + {% endfor -%} diff --git a/.forgejo/workflows/ansible-galaxy.yml b/.forgejo/workflows/ansible-galaxy.yml new file mode 100644 index 0000000..c853bd6 --- /dev/null +++ b/.forgejo/workflows/ansible-galaxy.yml @@ -0,0 +1,45 @@ +--- + +name: Ansible Galaxy + +on: # noqa yaml[truthy] + push: + tags: + - 'v*' + +jobs: + deploy: + runs-on: docker + steps: + + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + apt update; apt install --yes python3-pip patch + pip3 install --break-system-packages ansible aar-doc + + - name: Patch aar-doc + run: | + cd /usr/local/lib/python3.*/dist-packages/ + patch -p0 < $OLDPWD/.forgejo/workflows/aardoc.patch + + - name: Set version in galaxy.yml + run: | + VERSION=${GITHUB_REF#refs/tags/v} + sed -re "s/^version:.*$/version: ${VERSION}/" -i galaxy.yml + + - name: Generate metadata, readme and defaults from argument_spec + run: | + set -euo pipefail + for r in roles/*; do + aar-doc --output-mode replace $r markdown + aar-doc --output-mode replace $r defaults + done + + - name: Upload collection to Ansible Galaxy + env: + GALAXY_API_KEY: ${{ secrets.GALAXY_API_KEY }} + run: | + ansible-galaxy collection build + ansible-galaxy collection publish --api-key=${GALAXY_API_KEY} s3lph-conference*tar.gz diff --git a/.forgejo/workflows/ansible-lint.yml b/.forgejo/workflows/ansible-lint.yml new file mode 100644 index 0000000..5079e39 --- /dev/null +++ b/.forgejo/workflows/ansible-lint.yml @@ -0,0 +1,35 @@ +--- + +name: Ansible Lint +on: [push, pull_request] # noqa yaml[truthy] + +jobs: + build: + runs-on: docker + + steps: + + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + apt update; apt install --yes python3-pip patch + pip3 install --break-system-packages ansible-lint aar-doc + + - name: Patch aar-doc + run: | + cd /usr/local/lib/python3.*/dist-packages/ + patch -p0 < $OLDPWD/.forgejo/workflows/aardoc.patch + + + - name: Generate metadata, readme and defaults from argument_spec + run: | + set -euo pipefail + for r in roles/*; do + aar-doc --output-mode replace $r markdown + aar-doc --output-mode replace $r defaults + done + + - name: Run ansible-lint + run: | + ansible-lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02022ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.ansible/ +s3lph-conference*.tar.gz diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..6b658bd --- /dev/null +++ b/.yamllint @@ -0,0 +1,9 @@ +--- + +extends: default + +rules: + line-length: disable + +ignore: + - "**/defaults/" diff --git a/README.md b/README.md new file mode 100644 index 0000000..a069796 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Ansible Collection - s3lph.conference + +Documentation for the collection. diff --git a/galaxy.yml b/galaxy.yml new file mode 100644 index 0000000..e658afc --- /dev/null +++ b/galaxy.yml @@ -0,0 +1,70 @@ +--- +# SPDX-License-Identifier: MIT-0 +### REQUIRED +# The namespace of the collection. This can be a company/brand/organization or product namespace under which all +# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with +# underscores or numbers and cannot contain consecutive underscores +namespace: s3lph + +# The name of the collection. Has the same character restrictions as 'namespace' +name: conference + +# The version of the collection. Must be compatible with semantic versioning +version: 0.0.1 + +# The path to the Markdown (.md) readme file. This path is relative to the root of the collection +readme: README.md + +# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url) +# @nicks:irc/im.site#channel' +authors: + - s3lph <s3lph@kabelsalat.ch> + +### OPTIONAL but strongly recommended +# A short summary description of the collection +description: Install Pretix and Pretalx + +# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only +# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' +license: + - MIT + +# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character +# requirements as 'namespace' and 'name' +tags: + - application + - pretix + - pretalx + +# Collections that this collection requires to be installed for it to be usable. The key of the dict is the +# collection label 'namespace.name'. The value is a version range +# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version +# range specifiers can be set and are separated by ',' +dependencies: + community.podman: ">=1.16.2,<2.0.0" + community.postgresql: ">=3.10.2,<4.0.0" + +# The URL of the originating SCM repository +repository: https://git.kabelsalat.ch/s3lph/ansible-collection-conference + +# The URL to any online docs +documentation: https://git.kabelsalat.ch/s3lph/ansible-collection-conference + +# The URL to the homepage of the collection/project +homepage: https://git.kabelsalat.ch/s3lph/ansible-collection-conference + +# The URL to the collection issue tracker +issues: https://git.kabelsalat.ch/s3lph/ansible-collection-conference/issues + +# A list of file glob-like patterns used to filter any files or directories that should not be included in the build +# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This +# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', +# and '.git' are always filtered. Mutually exclusive with 'manifest' +build_ignore: [] + +# A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a +# list of MANIFEST.in style +# L(directives,https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands). The key +# 'omit_default_directives' is a boolean that controls whether the default directives are used. Mutually exclusive +# with 'build_ignore' +# manifest: null diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 0000000..ab3941d --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1,52 @@ +--- +# Collections must specify a minimum required ansible version to upload +# to galaxy +requires_ansible: '>=2.15' + +# Content that Ansible needs to load from another location or that has +# been deprecated/removed +# plugin_routing: +# action: +# redirected_plugin_name: +# redirect: ns.col.new_location +# deprecated_plugin_name: +# deprecation: +# removal_version: "4.0.0" +# warning_text: | +# See the porting guide on how to update your playbook to +# use ns.col.another_plugin instead. +# removed_plugin_name: +# tombstone: +# removal_version: "2.0.0" +# warning_text: | +# See the porting guide on how to update your playbook to +# use ns.col.another_plugin instead. +# become: +# cache: +# callback: +# cliconf: +# connection: +# doc_fragments: +# filter: +# httpapi: +# inventory: +# lookup: +# module_utils: +# modules: +# netconf: +# shell: +# strategy: +# terminal: +# test: +# vars: + +# Python import statements that Ansible needs to load from another location +# import_redirection: +# ansible_collections.ns.col.plugins.module_utils.old_location: +# redirect: ansible_collections.ns.col.plugins.module_utils.new_location + +# Groups of actions/modules that take a common set of options +# action_groups: +# group_name: +# - module1 +# - module2 diff --git a/roles/pretalx/handlers/main.yml b/roles/pretalx/handlers/main.yml new file mode 100644 index 0000000..f6cca73 --- /dev/null +++ b/roles/pretalx/handlers/main.yml @@ -0,0 +1,7 @@ +--- + +- name: Restart Pretalx + community.podman.podman_container: + name: pretalx + state: started + restart: true diff --git a/roles/pretalx/meta/argument_specs.yml b/roles/pretalx/meta/argument_specs.yml new file mode 100644 index 0000000..38dccd6 --- /dev/null +++ b/roles/pretalx/meta/argument_specs.yml @@ -0,0 +1,181 @@ +--- + +argument_specs: + + main: + version_added: "0.0.1" + short_description: Install and configure Pretalx. + description: + - Install and configure the L(Pretalx,https://pretalx.com/p/about/) conference scheduling software. + - "Execution of this role can be limited using the following tags:" + - "C(role::pretalx:install): Install Pretalx, Valkey and PostgreSQL" + - "C(role::pretalx): Apply all of the above." + author: s3lph + options: + + # Container image settings + pretalx_image: + description: + - OCI Container image name for Pretalx + type: str + default: docker.io/pretalx/standalone + pretalx_image_tag: + description: + - OCI Container image tag for Pretalx + type: str + default: "v2024.3.1" + pretalx_cache_image: + description: + - OCI Container image name for Valkey + type: str + default: docker.io/valkey/valkey + pretalx_cache_image_tag: + description: + - OCI Container image tag for Valkey + type: str + default: "8" + pretalx_http_hostaddr: + description: + - Host address to map to Pretalx http port + type: str + default: "[::1]" + pretalx_http_hostport: + description: + - Host port to map to Pretalx http port + type: int + default: 8081 + pretalx_container_uid: + description: + - UID under which Pretalx runs inside the container. + - On the host, this is offset by O(pretalx_subuid_begin). + - You should not need to change this. + type: int + default: 999 + pretalx_container_gid: + description: + - GID under which Pretalx runs inside the container. + - On the host, this is offset by O(pretalx_subgid_begin). + - You should not need to change this. + type: int + default: 999 + pretalx_watchtower_enabled: + description: + - "Whether to enable automatic container updates through L(Watchtower,https://containrrr.dev/watchtower/)." + - "If this is true, you should set O(pretalx_image_tag) to something other than V(latest) or V(stable)." + - "See also: M(s3lph.conference.watchtower)." + type: bool + default: false + + # System user settings + pretalx_system_group: + description: + - Name of the Pretalx system group. + type: str + default: pretalx + pretalx_system_user: + description: + - Name of the Pretalx system user. + type: str + default: pretalx + pretalx_system_home: + description: + - Home of the Pretalx system user. + - The persistent Pretalx data will be stored here. + type: str + default: /var/lib/pretalx + pretalx_subuid_begin: + description: + - First subordinate uid for the Pretalx system user. + type: int + default: 200000 + pretalx_subuid_count: + description: + - Number of subordinate uids for the Pretalx system user. + type: int + default: 65536 + pretalx_subgid_begin: + description: + - First subordinate gid for the Pretalx system user. + type: int + default: 200000 + pretalx_subgid_count: + description: + - Number of subordinate gids for the Pretalx system user. + type: int + default: 65536 + + # Database settings + pretalx_postgresql_username: + description: + - PostgreSQL username to be used by Pretalx. + type: str + default: pretalx + #pretalx_postgresql_password: + # description: + # - PostgreSQL password to be used by Pretalx. + # type: str + # required: true + pretalx_postgresql_database: + description: + - PostgreSQL database to be used by Pretalx. + type: str + default: pretalx + + # pretalx.cfg: main config section + pretalx_url: + description: + - Base URL of the Pretalx installation. + type: str + default: https://pretalx.example.org + pretalx_timezone: + description: + - Time zone of the Pretalx instance + type: str + default: UTC + + # pretalx.cfg: mail config section + pretalx_mail_from: + description: + - Address from which Pretalx sends e-mail. + type: str + default: pretalx@example.org + pretalx_mail_host: + description: + - Host over which Pretalx sends e-mail. + type: str + default: 172.17.0.1 + pretalx_mail_port: + description: + - Port over which Pretalx sends e-mail. + - "The default depends on the value if O(pretalx_tls_mode):" + - "O(pretalx_tls_mode=none): V(25)" + - "O(pretalx_tls_mode=tls): V(465)" + - "O(pretalx_tls_mode=starttls): V(587)" + type: int + default: 0 + pretalx_mail_tls_mode: + description: + - TLS mode to use when Pretalx sends e-mail. + type: str + choices: ["none", "tls", "starttls"] + default: "none" + pretalx_mail_username: + description: + - If present, the username Pretalx uses to send e-mail. + - "Only takes effect if O(pretalx_mail_password) is set as well." + type: str + default: null + pretalx_mail_password: + description: + - If present, the password Pretalx uses to send e-mail. + - "Only takes effect if O(pretalx_mail_username) is set as well." + type: str + default: null + + # pretalx.cfg: redis config section + pretalx_valkey_db_offset: + description: + - Valkey database index offset to be used by Pretalx. + - Pretalx uses this and the following two database indices. + type: int + default: 0 diff --git a/roles/pretalx/meta/main.yml b/roles/pretalx/meta/main.yml new file mode 100644 index 0000000..2770508 --- /dev/null +++ b/roles/pretalx/meta/main.yml @@ -0,0 +1,42 @@ +--- +# SPDX-License-Identifier: MIT +galaxy_info: + author: s3lph + description: Install and configure Pretalx. + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + issue_tracker_url: https://git.kabelsalat.ch/s3lph/ansible-collection-conference/issues + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: MIT + + min_ansible_version: "2.15" + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + platforms: + - name: Debian + versions: + - bullseye + - bookworm + - trixie + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] +# List your role dependencies here, one per line. Be sure to remove the '[]' above, +# if you add dependencies to this list. diff --git a/roles/pretalx/tasks/install.yml b/roles/pretalx/tasks/install.yml new file mode 100644 index 0000000..b9799d1 --- /dev/null +++ b/roles/pretalx/tasks/install.yml @@ -0,0 +1,110 @@ +--- + +- name: Install dependencies + ansible.builtin.package: + name: + - podman + - postgresql + - python3-psycopg2 + +- name: Create pretalx group + ansible.builtin.group: + name: "{{ pretalx_system_group }}" + +- name: Create pretalx user + ansible.builtin.user: + name: "{{ pretalx_system_user }}" + group: "{{ ptrealx_system_group }}" + home: "{{ pretalx_system_home }}" + password: '!' + shell: /usr/sbin/nologin + comment: Pretalx + +- name: Create subuid entry + ansible.builtin.lineinfile: + path: /etc/subuid + line: "{{ pretalx_system_user }}:{{ pretalx_subuid_begin }}:{{ pretalx_subuid_count }}" + regexp: "^{{ pretalx_system_user }}:" + create: true + owner: root + group: root + mode: "0644" + +- name: Create subgid entry + ansible.builtin.lineinfile: + path: /etc/subgid + line: "{{ pretalx_system_user }}:{{ pretalx_subgid_begin }}:{{ pretalx_subgid_count }}" + regexp: "^{{ pretalx_system_user }}:" + create: true + owner: root + group: root + mode: "0644" + +- name: Create pretalx directories + ansible.builtin.file: + path: "{{ pretalx_system_home }}/{{ item }}" + state: directory + owner: "{{ pretalx_subuid_begin + pretalx_container_uid }}" + group: "{{ pretalx_subgid_begin + pretalx_container_gid }}" + mode: "0750" + loop: + - data + - public + +- name: Create PostgreSQL user + community.postgresql.postgresql_user: + name: "{{ pretalx_postgresql_username }}" + #password: "{{ pretalx_postgresql_password }}" + #no_password_changes: true + +- name: Create PostgreSQL database + community.postgresql.postgresql_db: + name: "{{ pretalx_postgresql_database }}" + owner: "{{ pretalx_postgresql_username }}" + +- name: Create Pretalx configuration directory + ansible.builtin.file: + path: /etc/pretalx + owner: "{{ pretalx_system_user }}" + group: "{{ pretalx_system_group }}" + mode: "0755" + state: directory + +- name: Render Pretalx configuration + ansible.builtin.template: + src: etc/pretalx/pretalx.cfg.j2 + dest: /etc/pretalx/pretalx.cfg + owner: "{{ pretalx_system_user }}" + group: "{{ pretalx_system_group }}" + mode: "0700" + notify: Restart Pretalx + +- name: Create pretalx-cache container + community.podman.podman_container: + name: pretalx-cache + image: "{{ pretalx_cache_image }}:{{ pretalx_cache_image_tag }}" + restart_policy: unless-stopped + labels: + com.centurylinklabs.watchtower.enable: "{{ pretalx_watchtower_enabled | ternary('true', 'false') }}" + subuidname: "{{ pretix_system_user }}" + subgidname: "{{ pretix_system_user }}" + +- name: Create pretalx container + community.podman.podman_container: + name: pretalx + image: "{{ pretalx_image }}:{{ pretalx_image_tag }}" + restart_policy: unless-stopped + ports: + - "{{ pretalx_http_hostaddr }}:{{ pretalx_http_hostport }}:80" + volumes: + - "/etc/pretalx:/etc/pretalx:ro" + - "{{ pretalx_system_home }}/data:/data" + - "{{ pretalx_system_home }}/public:/public" + - "/run/postgresql:/run/postgresql" + env: + PRETALX_FILESYSTEM_MEDIA: /public/media + PRETALX_FILESYSTEM_STATIC: /public/static + labels: + com.centurylinklabs.watchtower.enable: "{{ pretalx_watchtower_enabled | ternary('true', 'false') }}" + subuidname: "{{ pretix_system_user }}" + subgidname: "{{ pretix_system_user }}" diff --git a/roles/pretalx/tasks/main.yml b/roles/pretalx/tasks/main.yml new file mode 100644 index 0000000..be86796 --- /dev/null +++ b/roles/pretalx/tasks/main.yml @@ -0,0 +1,7 @@ +--- + +- name: Install Pretalx + ansible.builtin.import_tasks: install.yml + tags: + - "role::pretalx" + - "role::pretalx:install" diff --git a/roles/pretalx/templates/etc/pretalx/pretalx.cfg.j2 b/roles/pretalx/templates/etc/pretalx/pretalx.cfg.j2 new file mode 100644 index 0000000..7ccb675 --- /dev/null +++ b/roles/pretalx/templates/etc/pretalx/pretalx.cfg.j2 @@ -0,0 +1,47 @@ +{{ ansible_managed | comment }} + +[site] +debug=false +url={{ pretalx_url }} + +[locale] +time_zone={{ pretalx_timezone }}Europe/Zurich + +[database] +backend=postgresql +name={{ pretalx_postgresql_database }} +user={{ pretalx_postgresql_user }} +; password=********* +host= + +[mail] +from={{ pretalx_mail_from }} +host={{ pretalx_mail_host }} +{% if pretalx_mail_tls_mode == 'starttls' %} +port={{ pretalx_mail_port or 587 }} +tls=on +ssl=off +{% elif pretalx_mail_tls_mode == 'tls' %} +port={{ pretalx_mail_port or 465 }} +tls=off +ssl=on +{% else %} +port={{ pretalx_mail_port or 25 }} +tls=off +ssl=off +{% endif %} +{% if pretalx_mail_username is not none and pretalx_mail_password is not none %} +user={{ pretalx_mail_username }} +password={{ pretalx_mail_password }} +{% endif %} + + +[redis] +location=redis://pretalx-cache:6379/{{ pretalx_valkey_db_offset }} +; Remove the following line if you are unsure about your redis' security +; to reduce impact if redis gets compromised. +sessions=true + +[celery] +backend=redis://pretalx-cache:6379/{{ pretalx_valkey_db_offset + 1 }} +broker=redis://pretalx-cache:6379/{{ pretalx_valkey_db_offset + 2 }} diff --git a/roles/pretix/handlers/main.yml b/roles/pretix/handlers/main.yml new file mode 100644 index 0000000..4b6a3a3 --- /dev/null +++ b/roles/pretix/handlers/main.yml @@ -0,0 +1,7 @@ +--- + +- name: Restart Pretix + community.podman.podman_container: + name: pretix + state: started + restart: true diff --git a/roles/pretix/meta/argument_specs.yml b/roles/pretix/meta/argument_specs.yml new file mode 100644 index 0000000..6d6b5d1 --- /dev/null +++ b/roles/pretix/meta/argument_specs.yml @@ -0,0 +1,198 @@ +--- + +argument_specs: + + main: + version_added: "0.0.1" + short_description: Install and configure Pretix. + description: + - Install and configure the L(Pretix,https://pretix.eu) conference ticketing software. + - "Execution of this role can be limited using the following tags:" + - "C(role::pretix:install): Install Pretix, Valkey and PostgreSQL" + - "C(role::pretix): Apply all of the above." + author: s3lph + options: + + # Container image settings + pretix_image: + description: + - OCI Container image name for Pretix + type: str + default: docker.io/pretix/standalone + pretix_image_tag: + description: + - OCI Container image tag for Pretix + type: str + default: "2025.2" + pretix_cache_image: + description: + - OCI Container image name for Redis + type: str + default: docker.io/valkey/valkey + pretix_cache_image_tag: + description: + - OCI Container image tag for Redis + type: str + default: "8" + pretix_http_hostpaddr: + description: + - Host address to map to Pretix http port + type: str + default: "[::1]" + pretix_http_hostport: + description: + - Host port to map to Pretix http port + type: int + default: 8080 + pretix_container_uid: + description: + - UID under which Pretix runs inside the container. + - On the host, this is offset by O(pretix_subuid_begin). + - You should not need to change this. + type: int + default: 15371 + pretix_container_gid: + description: + - GID under which Pretix runs inside the container. + - On the host, this is offset by O(pretix_subgid_begin). + - You should not need to change this. + type: int + default: 15371 + pretix_watchtower_enabled: + description: + - "Whether to enable automatic container updates through L(Watchtower,https://containrrr.dev/watchtower/)." + - "If this is true, you should set O(pretix_image_tag) to something other than V(latest) or V(stable)." + - "See also: M(s3lph.conference.watchtower)." + type: bool + default: false + + # System user settings + pretix_system_group: + description: + - Name of the Pretix system group. + type: str + default: pretix + pretix_system_user: + description: + - Name of the Pretix system user. + type: str + default: pretix + pretix_system_home: + description: + - Home of the Pretix system user. + - The persistent Pretix data will be stored here. + type: str + default: /var/lib/pretix + pretix_subuid_begin: + description: + - First subordinate uid for the Pretix system user. + type: int + default: 100000 + pretix_subuid_count: + description: + - Number of subordinate uids for the Pretix system user. + type: int + default: 65536 + pretix_subgid_begin: + description: + - First subordinate gid for the Pretix system user. + type: int + default: 100000 + pretix_subgid_count: + description: + - Number of subordinate gids for the Pretix system user. + type: int + default: 65536 + + # Database settings + pretix_postgresql_username: + description: + - PostgreSQL username to be used by Pretix. + type: str + default: pretix + #pretix_postgresql_password: + # description: + # - PostgreSQL password to be used by Pretix. + # type: str + # required: true + pretix_postgresql_database: + description: + - PostgreSQL database to be used by Pretix. + type: str + default: pretix + + # pretix.cfg: main config section + pretix_instance_name: + description: + - Name of the Pretix installation. + type: str + default: My pretix installation + pretix_url: + description: + - Base URL of the Pretix installation. + type: str + default: https://pretix.example.org + pretix_currency: + description: + - Currency to use in the Pretix installation. + type: str + default: EUR + pretix_registration: + description: + - Whether user signup in the Pretix installation should be enabled. + type: bool + default: false + + # pretix.cfg: mail config section + pretix_mail_from: + description: + - Address from which Pretix sends e-mail. + type: str + default: pretix@example.org + pretix_mail_host: + description: + - Host over which Pretix sends e-mail. + type: str + default: 172.17.0.1 + pretix_mail_port: + description: + - Port over which Pretix sends e-mail. + - "The default depends on the value if O(pretix_tls_mode):" + - "O(pretix_tls_mode=none): V(25)" + - "O(pretix_tls_mode=tls): V(465)" + - "O(pretix_tls_mode=starttls): V(587)" + type: int + default: 0 + pretix_mail_tls_mode: + description: + - TLS mode to use when Pretix sends e-mail. + type: str + choices: ["none", "tls", "starttls"] + default: "none" + pretix_mail_username: + description: + - If present, the username Pretix uses to send e-mail. + - "Only takes effect if O(pretix_mail_password) is set as well." + type: str + default: null + pretix_mail_password: + description: + - If present, the password Pretix uses to send e-mail. + - "Only takes effect if O(pretix_mail_username) is set as well." + type: str + default: null + + # pretix.cfg: redis config section + pretix_valkey_db_offset: + description: + - Valkey database index offset to be used by Pretix. + - Pretix uses this and the following two database indices. + type: int + default: 0 + + # Cronjob + pretix_cron: + description: + - The cron expression of when to execute Pretix jobs. + type: str + default: "*/15 * * * *" diff --git a/roles/pretix/meta/main.yml b/roles/pretix/meta/main.yml new file mode 100644 index 0000000..13e5d04 --- /dev/null +++ b/roles/pretix/meta/main.yml @@ -0,0 +1,42 @@ +--- +# SPDX-License-Identifier: MIT +galaxy_info: + author: s3lph + description: Install and configure Pretix. + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + issue_tracker_url: https://git.kabelsalat.ch/s3lph/ansible-collection-conference/issues + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: MIT + + min_ansible_version: "2.15" + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + platforms: + - name: Debian + versions: + - bullseye + - bookworm + - trixie + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] +# List your role dependencies here, one per line. Be sure to remove the '[]' above, +# if you add dependencies to this list. diff --git a/roles/pretix/tasks/install.yml b/roles/pretix/tasks/install.yml new file mode 100644 index 0000000..f70b128 --- /dev/null +++ b/roles/pretix/tasks/install.yml @@ -0,0 +1,119 @@ +--- + +- name: Install dependencies + ansible.builtin.package: + name: + - podman + - postgresql + - python3-psycopg2 + +- name: Create pretix group + ansible.builtin.group: + name: "{{ pretix_system_group }}" + +- name: Create pretix user + ansible.builtin.user: + name: "{{ pretix_system_user }}" + group: "{{ pretix_system_group }}" + home: "{{ pretix_system_home }}" + password: '!' + shell: /usr/sbin/nologin + comment: Pretix + +- name: Create subuid entry + ansible.builtin.lineinfile: + path: /etc/subuid + line: "{{ pretix_system_user }}:{{ pretix_subuid_begin }}:{{ pretix_subuid_count }}" + regexp: "^{{ pretix_system_user }}:" + create: true + owner: root + group: root + mode: "0644" + +- name: Create subgid entry + ansible.builtin.lineinfile: + path: /etc/subgid + line: "{{ pretix_system_user }}:{{ pretix_subgid_begin }}:{{ pretix_subgid_count }}" + regexp: "^{{ pretix_system_user }}:" + create: true + owner: root + group: root + mode: "0644" + +- name: Create pretix data directory + ansible.builtin.file: + path: "{{ pretix_system_home }}/data" + state: directory + owner: "{{ pretix_subuid_begin + pretix_container_uid }}" + group: "{{ pretix_subgid_begin + pretix_container_gid }}" + mode: "0700" + +- name: Create PostgreSQL user + community.postgresql.postgresql_user: + name: "{{ pretix_postgresql_username }}" + #password: "{{ pretix_postgresql_password }}" + #no_password_changes: true + +- name: Create PostgreSQL database + community.postgresql.postgresql_db: + name: "{{ pretix_postgresql_database }}" + owner: "{{ pretix_postgresql_username }}" + +- name: Create Pretix configuration directory + ansible.builtin.file: + path: /etc/pretix + owner: "{{ pretix_system_user }}" + group: "{{ pretix_system_group }}" + mode: "0755" + state: directory + +- name: Render Pretix configuration + ansible.builtin.template: + src: etc/pretix/pretix.cfg.j2 + dest: /etc/pretix/pretix.cfg + owner: "{{ pretix_system_user }}" + group: "{{ pretix_system_group }}" + mode: "0700" + notify: Restart Pretix + +- name: Create pretix-cache container + containers.podman.podman_container: + name: pretix-cache + image: "{{ pretix_cache_image }}:{{ pretix_cache_image_tag }}" + restart_policy: unless-stopped + labels: + com.centurylinklabs.watchtower.enable: "{{ pretix_watchtower_enabled | ternary('true', 'false') }}" + subuidname: "{{ pretix_system_user }}" + subgidname: "{{ pretix_system_user }}" + +- name: Create pretix container + containers.podman.podman_container: + name: pretix + image: "{{ pretix_image }}:{{ pretix_image_tag }}" + command: "all" + restart_policy: unless-stopped + ports: + - "{{ pretix_http_hostaddr }}:{{ pretix_http_hostport }}:80" + volumes: + - "/etc/pretix:/etc/pretix:ro" + - "{{ pretix_system_home }}/data:/data" + - "/run/postgresql:/run/postgresql" + sysctl: + net.core.somaxconn: "4096" + subuidname: "{{ pretix_system_user }}" + subgidname: "{{ pretix_system_user }}" + labels: + com.centurylinklabs.watchtower.enable: "{{ pretix_watchtower_enabled | ternary('true', 'false') }}" + +- name: Run Pretix upgrade + containers.podman.podman_container_exec: + container: pretix + command: pretix upgrade + +- name: Render Pretix cronjob + ansible.builtin.template: + src: etc/cron.d/pretix.j2 + dest: /etc/cron.d/pretix + owner: root + group: root + mode: "0644" diff --git a/roles/pretix/tasks/main.yml b/roles/pretix/tasks/main.yml new file mode 100644 index 0000000..2b5b835 --- /dev/null +++ b/roles/pretix/tasks/main.yml @@ -0,0 +1,7 @@ +--- + +- name: Install Pretix + ansible.builtin.import_tasks: install.yml + tags: + - "role::pretix" + - "role::pretix:install" diff --git a/roles/pretix/templates/etc/cron.d/pretix.j2 b/roles/pretix/templates/etc/cron.d/pretix.j2 new file mode 100644 index 0000000..bc3d8fc --- /dev/null +++ b/roles/pretix/templates/etc/cron.d/pretix.j2 @@ -0,0 +1,3 @@ +{{ ansible_managed | comment }} + +{{ pretix_cron }} {{ pretix_system_user }} podmap exec pretix cron diff --git a/roles/pretix/templates/etc/pretix/pretix.cfg.j2 b/roles/pretix/templates/etc/pretix/pretix.cfg.j2 new file mode 100644 index 0000000..bd4e281 --- /dev/null +++ b/roles/pretix/templates/etc/pretix/pretix.cfg.j2 @@ -0,0 +1,47 @@ +{{ ansible_managed | comment }} + +instance_name={{ pretix_instance_name }} +url={{ pretix_url }} +currency={{ pretix_currency }} +datadir=/data +trust_x_forwarded_for=on +trust_x_forwarded_proto=on +registration={{ pretix_registration | ternary('on', 'off') }} + +[database] +backend=postgresql +name={{ pretix_postgresql_database }} +user={{ pretix_postgresql_user }} +host= + +[mail] +from={{ pretix_mail_from }} +host={{ pretix_mail_host }} +{% if pretix_mail_tls_mode == 'starttls' %} +port={{ pretix_mail_port or 587 }} +tls=on +ssl=off +{% elif pretix_mail_tls_mode == 'tls' %} +port={{ pretix_mail_port or 465 }} +tls=off +ssl=on +{% else %} +port={{ pretix_mail_port or 25 }} +tls=off +ssl=off +{% endif %} +{% if pretix_mail_username is not none and pretix_mail_password is not none %} +user={{ pretix_mail_username }} +password={{ pretix_mail_password }} +{% endif %} + + +[redis] +location=redis://pretix-cache:6379/{{ pretix_valkey_db_offset }} +; Remove the following line if you are unsure about your redis' security +; to reduce impact if redis gets compromised. +sessions=true + +[celery] +backend=redis://pretix-cache:6379/{{ pretix_valkey_db_offset + 1 }} +broker=redis://pretix-cache:6379/{{ pretix_valkey_db_offset + 2 }} diff --git a/roles/watchtower/meta/argument_specs.yml b/roles/watchtower/meta/argument_specs.yml new file mode 100644 index 0000000..8a6ad15 --- /dev/null +++ b/roles/watchtower/meta/argument_specs.yml @@ -0,0 +1,42 @@ +--- + +argument_specs: + + main: + version_added: "0.0.1" + short_description: Install and configure Watchtower. + description: + - Install and configure L(Watchtower,https://containrrr.dev/watchtower/). + - "Execution of this role can be limited using the following tags:" + - "C(role::watchtower:install): Install Podman and Watchtower" + - "C(role::watchtower): Apply all of the above." + author: s3lph + options: + + watchtower_image: + description: + - The name of the OCI image to run. + - "See also: O(watchtower_image_tag)." + type: str + default: docker.io/containrrr/watchtower + watchtower_image_tag: + description: + - The tag of the OCI image to run + - "See also: O(watchtower_image)." + type: str + default: latest + watchtower_schedule: + description: + - Cron expression of when containers should be updated. + - If omitted, default to once every 24h without specifying a point in time. + type: str + watchtower_label_enable: + description: + - Whether Watchtower support has to be enabled explicitly via container labels. + type: bool + default: true + watchtower_update_self: + description: + - Whether Watchtower should update itself as well. + type: bool + default: true diff --git a/roles/watchtower/meta/main.yml b/roles/watchtower/meta/main.yml new file mode 100644 index 0000000..0889655 --- /dev/null +++ b/roles/watchtower/meta/main.yml @@ -0,0 +1,42 @@ +--- +# SPDX-License-Identifier: MIT +galaxy_info: + author: s3lph + description: Install and configure Watchtower. + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + issue_tracker_url: https://git.kabelsalat.ch/s3lph/ansible-collection-conference/issues + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: MIT + + min_ansible_version: "2.15" + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + platforms: + - name: Debian + versions: + - bullseye + - bookworm + - trixie + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] +# List your role dependencies here, one per line. Be sure to remove the '[]' above, +# if you add dependencies to this list. diff --git a/roles/watchtower/tasks/install.yml b/roles/watchtower/tasks/install.yml new file mode 100644 index 0000000..b12e367 --- /dev/null +++ b/roles/watchtower/tasks/install.yml @@ -0,0 +1,25 @@ +--- + +- name: Install dependencies + ansible.builtin.package: + name: + - podman + +- name: Enable podman socket + ansible.builtin.systemd_service: + name: podman.socket + state: started + enabled: true + +- name: Create Watchtower container + community.podman.podman_container: + name: watchtower + image: "{{ watchtower_image }}:{{ watchtower_image_tag }}" + env: + WATCHTOWER_SCHEDULE: "{{ watchtower_schedule | default(omit) }}" + WATCHTOWER_LABEL_ENABLE: "{{ watchtower_label_enable | default(omit) }}" + restart_policy: unless-stopped + volumes: + - "/run/podman/podman.sock:/var/run/docker.sock" + labels: + com.centurylinklabs.watchtower.enable: "{{ watchtower_update_self | ternary('true', 'false') }}" diff --git a/roles/watchtower/tasks/main.yml b/roles/watchtower/tasks/main.yml new file mode 100644 index 0000000..a0e6fc5 --- /dev/null +++ b/roles/watchtower/tasks/main.yml @@ -0,0 +1,7 @@ +--- + +- name: Install Watchtower + ansible.builtin.import_tasks: install.yml + tags: + - "role::watchtower" + - "role::watchtower:install"