Files
Slavi PantaleevandClaude Opus 5.5 bff3f54d53 Harden pre-start container cleanup against name-release races
Containers are created with `--rm`, so Docker removes them asynchronously after they stop.
The pre-start `docker rm` returns immediately while that removal is still in progress
(its error is suppressed), and the following `docker create --name` can fail with
"The container name ... is already in use".

This is easy to hit since systemd_service_manager switched its default restart mode to `all-at-once`:
many containers stop and start at the same time and Docker falls behind on removals.
The playbook then fails at "Restart systemd services needing restart at once",
even though the services recover on their own (`Restart=always`) a little later.

The external roles (MASH, devture) were hardened against this in 2026-03
(see mother-of-all-self-hosting/ansible-role-valkey@0ba79c0), but the roles in `roles/custom` never were.
This ports the same bounded cleanup loop to them: stop, force-remove,
and wait until Docker releases the name, failing explicitly after a timeout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 19:04:04 +03:00

183 lines
12 KiB
YAML

# SPDX-FileCopyrightText: 2024 MDAD project contributors
# SPDX-FileCopyrightText: 2024 - 2026 Catalan Lover <catalanlover@protonmail.com>
# SPDX-FileCopyrightText: 2024 - 2025 Slavi Pantaleev
# SPDX-FileCopyrightText: 2024 Suguru Hirahara
#
# SPDX-License-Identifier: AGPL-3.0-or-later
---
# A moderation tool for Matrix
# Project source code URL: https://github.com/the-draupnir-project/Draupnir
matrix_appservice_draupnir_for_all_enabled: true
# renovate: datasource=docker depName=ghcr.io/the-draupnir-project/draupnir
matrix_appservice_draupnir_for_all_version: "v3.2.0"
matrix_appservice_draupnir_for_all_container_image_self_build: false
matrix_appservice_draupnir_for_all_container_image_self_build_repo: "https://github.com/the-draupnir-project/Draupnir.git"
matrix_appservice_draupnir_for_all_container_image_registry_prefix: "{{ 'localhost/' if matrix_appservice_draupnir_for_all_container_image_self_build else matrix_appservice_draupnir_for_all_container_image_registry_prefix_upstream }}"
matrix_appservice_draupnir_for_all_container_image_registry_prefix_upstream: "{{ matrix_appservice_draupnir_for_all_container_image_registry_prefix_upstream_default }}"
matrix_appservice_draupnir_for_all_container_image_registry_prefix_upstream_default: "ghcr.io/"
matrix_appservice_draupnir_for_all_container_image: "{{ matrix_appservice_draupnir_for_all_container_image_registry_prefix }}{{ matrix_appservice_draupnir_for_all_container_image_registry_namespace_identifier }}:{{ matrix_appservice_draupnir_for_all_version }}"
matrix_appservice_draupnir_for_all_container_image_registry_namespace_identifier: "the-draupnir-project/draupnir"
matrix_appservice_draupnir_for_all_base_path: "{{ matrix_base_data_path }}/draupnir-for-all"
matrix_appservice_draupnir_for_all_config_path: "{{ matrix_appservice_draupnir_for_all_base_path }}/config"
matrix_appservice_draupnir_for_all_data_path: "{{ matrix_appservice_draupnir_for_all_base_path }}/data"
matrix_appservice_draupnir_for_all_container_src_files_path: "{{ matrix_appservice_draupnir_for_all_base_path }}/docker-src"
matrix_appservice_draupnir_for_all_container_network: ""
matrix_appservice_draupnir_for_all_container_additional_networks: "{{ matrix_appservice_draupnir_for_all_container_additional_networks_auto + matrix_appservice_draupnir_for_all_container_additional_networks_custom }}"
matrix_appservice_draupnir_for_all_container_additional_networks_auto: []
matrix_appservice_draupnir_for_all_container_additional_networks_custom: []
# A list of extra arguments to pass to the container
matrix_appservice_draupnir_for_all_container_extra_arguments: []
# Controls how long the pre-start cleanup should wait for stale container-name release
# before failing startup explicitly.
matrix_appservice_draupnir_for_all_container_cleanup_timeout_seconds: "{{ devture_systemd_docker_base_container_cleanup_timeout_seconds }}"
# Controls the delay between pre-start cleanup retries.
matrix_appservice_draupnir_for_all_container_cleanup_retry_delay_seconds: "{{ devture_systemd_docker_base_container_cleanup_retry_delay_seconds }}"
# Shell command used in the systemd pre-start phase to deterministically remove a stale
# container and wait until Docker releases its name.
matrix_appservice_draupnir_for_all_container_cleanup_command: |
i=0;
while [ "$i" -lt {{ matrix_appservice_draupnir_for_all_container_cleanup_timeout_seconds }} ]; do
{{ devture_systemd_docker_base_host_command_docker }} stop -t {{ devture_systemd_docker_base_container_stop_grace_time_seconds }} matrix-appservice-draupnir-for-all >/dev/null 2>&1 || true;
{{ devture_systemd_docker_base_host_command_docker }} rm -f matrix-appservice-draupnir-for-all >/dev/null 2>&1 || true;
if {{ devture_systemd_docker_base_host_command_docker }} container inspect matrix-appservice-draupnir-for-all >/dev/null 2>&1; then
i=$((i + 1));
sleep {{ matrix_appservice_draupnir_for_all_container_cleanup_retry_delay_seconds }};
continue;
fi;
if {{ devture_systemd_docker_base_host_command_docker }} info >/dev/null 2>&1; then
exit 0;
fi;
echo "Failed to query Docker daemon while cleaning up matrix-appservice-draupnir-for-all" >&2;
exit 1;
done;
echo "Failed to remove stale container matrix-appservice-draupnir-for-all after {{ matrix_appservice_draupnir_for_all_container_cleanup_timeout_seconds }} attempts" >&2;
exit 1
# List of systemd services that matrix-bot-draupnir.service depends on
matrix_appservice_draupnir_for_all_systemd_required_services_list: "{{ matrix_appservice_draupnir_for_all_systemd_required_services_list_default + matrix_appservice_draupnir_for_all_systemd_required_services_list_auto + matrix_appservice_draupnir_for_all_systemd_required_services_list_custom }}"
matrix_appservice_draupnir_for_all_systemd_required_services_list_default: "{{ [devture_systemd_docker_base_docker_service_name] if devture_systemd_docker_base_docker_service_name else [] }}"
matrix_appservice_draupnir_for_all_systemd_required_services_list_auto: []
matrix_appservice_draupnir_for_all_systemd_required_services_list_custom: []
# List of systemd services that matrix-bot-draupnir.service wants
matrix_appservice_draupnir_for_all_systemd_wanted_services_list: []
# Rolling tag: true if version doesn't match semver shape (vX.Y.Z with optional prerelease/build), false otherwise.
matrix_appservice_draupnir_for_all_rolling_tag: "{{ not (matrix_appservice_draupnir_for_all_version is match('^v[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$')) }}"
# Force restart the service on all runs only when both roles are enabled, both roles
# are using the same version string, and that version is a classified as a moving tag.
matrix_appservice_draupnir_for_all_force_restart: "{{
matrix_bot_draupnir_enabled | bool and
matrix_appservice_draupnir_for_all_enabled | bool and
matrix_bot_draupnir_version == matrix_appservice_draupnir_for_all_version and
matrix_appservice_draupnir_for_all_rolling_tag | bool
}}"
# This controls whether Zero Touch Deployment is enabled.
# When enabled, the playbook validates the related settings and only renders
# the configuration values Draupnir expects for this mode.
# This prevents invalid manual combinations from being passed through, since
# Draupnir requires `matrix_appservice_draupnir_for_all_config_adminRoom` to be
# unset and `matrix_appservice_draupnir_for_all_config_initialManager` to be a
# valid user ID.
# Zero Touch Deployment is recommended for all new deployments.
# Deployments that are exempt from this recommendation are assumed to be
# advanced setups with specific needs that require the flexibility of
# non-zero-touch-deployment mode.
# Note that enabling this on an existing deployment will cause the bot to recreate the admin room.
# Manual policy migration has to be done in that case so as not to break when access controls return to working order.
matrix_appservice_draupnir_for_all_zero_touch_deploy: false
# The room ID where people can use the bot. The bot has no access controls, so
# anyone in this room can use the bot - secure your room!
# This should be a room alias - not a matrix.to URL.
# Appservice mode, unlike bot mode, is not verbose in the admin room.
# This room is different for Appservice Mode compared to normal mode.
# In Appservice mode it provides functions like user management.
matrix_appservice_draupnir_for_all_config_adminRoom: "" # noqa var-naming
# This controls the MXID of who is invited to the admin room on its creation when using Zero Touch Deployment.
# This value is mutually exclusive with matrix_appservice_draupnir_for_all_config_adminRoom
# and the bot will crash if you attempt to set both at the same time.
matrix_appservice_draupnir_for_all_config_initialManager: "" # noqa var-naming
# Controls if the room state backing store is activated.
# Room state backing store makes restarts of the bot lightning fast as the bot does not suffer from amnesia.
# This config option has diminished improvements for bots on extremely fast homeservers or very very small bots on fast homeservers.
matrix_appservice_draupnir_for_all_config_roomStateBackingStore_enabled: false # noqa var-naming
matrix_appservice_draupnir_for_all_database_username: matrix_appservice_draupnir_for_all
matrix_appservice_draupnir_for_all_database_password: 'some-passsword'
matrix_appservice_draupnir_for_all_database_hostname: ''
matrix_appservice_draupnir_for_all_database_port: 5432
matrix_appservice_draupnir_for_all_database_name: matrix_appservice_draupnir_for_all
matrix_appservice_draupnir_for_all_database_sslmode: disable
matrix_appservice_draupnir_for_all_appservice_port: "9001"
matrix_appservice_draupnir_for_all_appservice_url: 'http://matrix-appservice-draupnir-for-all'
matrix_appservice_draupnir_for_all_database_connection_string: 'postgresql://{{ matrix_appservice_draupnir_for_all_database_username }}:{{ matrix_appservice_draupnir_for_all_database_password }}@{{ matrix_appservice_draupnir_for_all_database_hostname }}:{{ matrix_appservice_draupnir_for_all_database_port }}/{{ matrix_appservice_draupnir_for_all_database_name }}?sslmode={{ matrix_appservice_draupnir_for_all_database_sslmode }}'
matrix_appservice_draupnir_for_all_user_prefix: "draupnir_"
matrix_appservice_draupnir_for_all_registration_yaml: |
id: "draupnir-for-all"
as_token: "{{ matrix_appservice_draupnir_for_all_appservice_token }}"
hs_token: "{{ matrix_appservice_draupnir_for_all_homeserver_token }}"
url: "{{ matrix_appservice_draupnir_for_all_appservice_url }}:{{ matrix_appservice_draupnir_for_all_appservice_port }}"
sender_localpart: draupnir-main
namespaces:
users:
- exclusive: true
regex: '@{{ matrix_appservice_draupnir_for_all_user_prefix }}*'
rate_limited: false
matrix_appservice_draupnir_for_all_registration: "{{ matrix_appservice_draupnir_for_all_registration_yaml | from_yaml }}"
matrix_appservice_draupnir_for_all_configuration_appservice: "{{ lookup('template', 'templates/production-appservice.yaml.j2') | from_yaml }}"
# Default configuration template which covers the generic use case.
# You can customize it by controlling the various variables inside it.
#
# For a more advanced customization, you can extend the default (see `matrix_appservice_draupnir_for_all_configuration_extension_yaml`)
# or completely replace this variable with your own template.
matrix_appservice_draupnir_for_all_configuration_yaml: "{{ lookup('template', 'templates/production-bots.yaml.j2') }}"
matrix_appservice_draupnir_for_all_configuration_extension_yaml: |
# Your custom YAML configuration goes here.
# This configuration extends the default starting configuration (`matrix_appservice_draupnir_for_all_configuration_yaml`).
#
# You can override individual variables from the default configuration, or introduce new ones.
#
# If you need something more special, you can take full control by
# completely redefining `matrix_appservice_draupnir_for_all_configuration_yaml`.
matrix_appservice_draupnir_for_all_configuration_extension: "{{ matrix_appservice_draupnir_for_all_configuration_extension_yaml | from_yaml if matrix_appservice_draupnir_for_all_configuration_extension_yaml | from_yaml is mapping else {} }}"
# Holds the final configuration (a combination of the default and its extension).
# You most likely don't need to touch this variable. Instead, see `matrix_appservice_draupnir_for_all_configuration_yaml`.
matrix_appservice_draupnir_for_all_configuration: "{{ matrix_appservice_draupnir_for_all_configuration_yaml | from_yaml | combine(matrix_appservice_draupnir_for_all_configuration_extension, recursive=True) }}"
# matrix_appservice_draupnir_for_all_restart_necessary controls whether the service
# will be restarted (when true) or merely started (when false) by the
# systemd service manager role (when conditional restart is enabled).
#
# This value is automatically computed during installation based on whether
# any configuration files, the systemd service file, or the container image changed.
# The default of `false` means "no restart needed" — appropriate when the role's
# installation tasks haven't run (e.g., due to --tags skipping them).
matrix_appservice_draupnir_for_all_restart_necessary: false