fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
282
deployment/ansible/callback_plugins/README.md
Normal file
282
deployment/ansible/callback_plugins/README.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# Ansible Callback Plugin - default_with_clean_msg
|
||||
|
||||
**Stand:** 2025-11-07
|
||||
**Status:** Dokumentation des Custom Callback Plugins
|
||||
|
||||
---
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das `default_with_clean_msg` Callback Plugin erweitert Ansible's Standard-Output mit verbesserter Formatierung für multiline `msg` Felder. Multiline Nachrichten werden als lesbare Blöcke mit Borders angezeigt, anstatt als escaped Newline-Zeichen.
|
||||
|
||||
**Datei:** `deployment/ansible/callback_plugins/default_with_clean_msg.py`
|
||||
|
||||
---
|
||||
|
||||
## Zweck
|
||||
|
||||
### Problem
|
||||
|
||||
Ansible's Standard Callback Plugin zeigt multiline `msg` Felder so an:
|
||||
```
|
||||
"msg": "Line 1\nLine 2\nLine 3"
|
||||
```
|
||||
|
||||
Dies macht es schwierig, multiline Debug-Ausgaben zu lesen und zu kopieren.
|
||||
|
||||
### Lösung
|
||||
|
||||
Das Custom Plugin formatiert multiline Nachrichten als lesbare Blöcke:
|
||||
```
|
||||
================================================================================
|
||||
Line 1
|
||||
Line 2
|
||||
Line 3
|
||||
================================================================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Funktionalität
|
||||
|
||||
### Multiline Message Formatting
|
||||
|
||||
**Automatische Erkennung:**
|
||||
- Nur Nachrichten mit mehr als einer Zeile werden formatiert
|
||||
- Einzeilige Nachrichten bleiben unverändert
|
||||
|
||||
**Format:**
|
||||
- Border oben und unten (aus `=` Zeichen)
|
||||
- Maximale Border-Breite: 80 Zeichen
|
||||
- Farbcodierung entsprechend Task-Status
|
||||
|
||||
### Method Overrides
|
||||
|
||||
Das Plugin überschreibt folgende Methoden des Default Callbacks:
|
||||
|
||||
- `v2_playbook_on_task_start` - Task Start
|
||||
- `v2_runner_on_start` - Runner Start
|
||||
- `v2_runner_on_ok` - Erfolgreiche Tasks
|
||||
- `v2_runner_on_failed` - Fehlgeschlagene Tasks
|
||||
- `v2_runner_on_skipped` - Übersprungene Tasks
|
||||
- `v2_runner_on_unreachable` - Unerreichbare Hosts
|
||||
|
||||
**Grund:** Diese Methoden werden überschrieben, um Warnings zu vermeiden, die auftreten, wenn `get_option()` vor der vollständigen Initialisierung aufgerufen wird.
|
||||
|
||||
---
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### ansible.cfg
|
||||
|
||||
**Datei:** `deployment/ansible/ansible.cfg`
|
||||
|
||||
```ini
|
||||
[defaults]
|
||||
stdout_callback = default_with_clean_msg
|
||||
callback_plugins = ./callback_plugins
|
||||
```
|
||||
|
||||
**Wichtig:**
|
||||
- `stdout_callback` aktiviert das Plugin als Standard-Output
|
||||
- `callback_plugins` gibt den Pfad zu den Plugin-Dateien an
|
||||
|
||||
### Plugin-Datei
|
||||
|
||||
**Pfad:** `deployment/ansible/callback_plugins/default_with_clean_msg.py`
|
||||
|
||||
**Struktur:**
|
||||
- Erbt von `ansible.plugins.callback.default.CallbackModule`
|
||||
- Überschreibt spezifische Methoden
|
||||
- Fügt `_print_clean_msg()` Methode hinzu
|
||||
|
||||
---
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Automatisch
|
||||
|
||||
Das Plugin wird automatisch verwendet, wenn `ansible.cfg` korrekt konfiguriert ist:
|
||||
|
||||
```bash
|
||||
cd deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml playbooks/setup-infrastructure.yml
|
||||
```
|
||||
|
||||
### Manuell
|
||||
|
||||
Falls das Plugin nicht automatisch geladen wird:
|
||||
|
||||
```bash
|
||||
ansible-playbook \
|
||||
--callback-plugin ./callback_plugins \
|
||||
--stdout-callback default_with_clean_msg \
|
||||
-i inventory/production.yml \
|
||||
playbooks/setup-infrastructure.yml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Beispiel-Ausgabe
|
||||
|
||||
### Vorher (Standard Callback)
|
||||
|
||||
```
|
||||
ok: [server] => {
|
||||
"msg": "Container Status:\nNAME IMAGE COMMAND SERVICE CREATED STATUS PORTS\nproduction-php-1 localhost:5000/framework:latest \"/usr/local/bin/entr…\" php About a minute ago Restarting (255) 13 seconds ago"
|
||||
}
|
||||
```
|
||||
|
||||
### Nachher (Custom Callback)
|
||||
|
||||
```
|
||||
ok: [server] => {
|
||||
================================================================================
|
||||
Container Status:
|
||||
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
|
||||
production-php-1 localhost:5000/framework:latest "/usr/local/bin/entr…" php About a minute ago Restarting (255) 13 seconds ago
|
||||
================================================================================
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bekannte Limitationen
|
||||
|
||||
### Warnings bei Option Access
|
||||
|
||||
**Problem:** Frühere Versionen des Plugins riefen `get_option()` auf, bevor Ansible's Optionen vollständig initialisiert waren, was zu Warnings führte:
|
||||
|
||||
```
|
||||
[WARNING]: Failure using method (v2_playbook_on_task_start) in callback plugin: 'display_skipped_hosts'
|
||||
```
|
||||
|
||||
**Lösung:** Das Plugin überschreibt die problematischen Methoden direkt, ohne `get_option()` aufzurufen.
|
||||
|
||||
### Option-Checks
|
||||
|
||||
**Aktuell:** Das Plugin zeigt immer alle Hosts an (ok, changed, skipped), ohne Option-Checks.
|
||||
|
||||
**Grund:** Option-Checks würden `get_option()` erfordern, was Warnings verursacht.
|
||||
|
||||
**Workaround:** Falls Option-Checks benötigt werden, können sie nach vollständiger Initialisierung implementiert werden.
|
||||
|
||||
---
|
||||
|
||||
## Technische Details
|
||||
|
||||
### Inheritance
|
||||
|
||||
```python
|
||||
from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule
|
||||
|
||||
class CallbackModule(DefaultCallbackModule):
|
||||
CALLBACK_NAME = 'default_with_clean_msg'
|
||||
|
||||
def _print_clean_msg(self, result, color=C.COLOR_VERBOSE):
|
||||
# Custom formatting logic
|
||||
```
|
||||
|
||||
**Vorteil:** Erbt alle Standard-Funktionalität und erweitert nur die Formatierung.
|
||||
|
||||
### Method Overrides
|
||||
|
||||
**Warum Overrides?**
|
||||
|
||||
Die Standard-Methoden rufen `get_option()` auf, um zu prüfen, ob bestimmte Hosts angezeigt werden sollen. Dies schlägt fehl, wenn Optionen noch nicht initialisiert sind.
|
||||
|
||||
**Lösung:** Direkte Implementierung ohne Option-Checks:
|
||||
|
||||
```python
|
||||
def v2_runner_on_ok(self, result):
|
||||
# Eigene Implementierung ohne get_option()
|
||||
# ...
|
||||
self._print_clean_msg(result, color=color)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### Plugin testen
|
||||
|
||||
```bash
|
||||
# Plugin-Verzeichnis
|
||||
cd deployment/ansible/callback_plugins
|
||||
|
||||
# Syntax prüfen
|
||||
python3 -m py_compile default_with_clean_msg.py
|
||||
|
||||
# Mit Ansible testen
|
||||
cd ..
|
||||
ansible-playbook -i inventory/production.yml playbooks/check-container-status.yml
|
||||
```
|
||||
|
||||
### Plugin erweitern
|
||||
|
||||
**Neue Formatierung hinzufügen:**
|
||||
|
||||
1. `_print_clean_msg()` Methode erweitern
|
||||
2. Neue Formatierungslogik implementieren
|
||||
3. Tests durchführen
|
||||
|
||||
**Beispiel:**
|
||||
```python
|
||||
def _print_clean_msg(self, result, color=C.COLOR_VERBOSE):
|
||||
msg_body = result._result.get('msg')
|
||||
if isinstance(msg_body, str) and msg_body.strip():
|
||||
# Custom formatting logic here
|
||||
# ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Plugin wird nicht geladen
|
||||
|
||||
**Problem:** Plugin wird nicht verwendet, Standard-Output bleibt
|
||||
|
||||
**Lösung:**
|
||||
1. `ansible.cfg` prüfen:
|
||||
```ini
|
||||
stdout_callback = default_with_clean_msg
|
||||
callback_plugins = ./callback_plugins
|
||||
```
|
||||
|
||||
2. Plugin-Datei prüfen:
|
||||
```bash
|
||||
ls -la deployment/ansible/callback_plugins/default_with_clean_msg.py
|
||||
```
|
||||
|
||||
3. Syntax prüfen:
|
||||
```bash
|
||||
python3 -m py_compile default_with_clean_msg.py
|
||||
```
|
||||
|
||||
### Warnings erscheinen
|
||||
|
||||
**Problem:** Warnings wie `'display_skipped_hosts'`
|
||||
|
||||
**Lösung:** Plugin-Version prüfen - sollte Method Overrides ohne `get_option()` verwenden.
|
||||
|
||||
**Aktueller Stand:** Plugin verwendet direkte Overrides ohne Option-Checks.
|
||||
|
||||
---
|
||||
|
||||
## Referenz
|
||||
|
||||
- [Ansible Callback Plugins Documentation](https://docs.ansible.com/ansible/latest/plugins/callback.html)
|
||||
- [Ansible Callback Development Guide](https://docs.ansible.com/ansible/latest/dev_guide/developing_plugins.html#callback-plugins)
|
||||
- [Initial Deployment Troubleshooting](../docs/troubleshooting/initial-deployment-issues.md) - Problem 8: Ansible Debug Messages
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2025-11-07
|
||||
- Initial Version erstellt
|
||||
- Multiline Message Formatting implementiert
|
||||
- Method Overrides ohne Option-Checks
|
||||
- Warnings behoben
|
||||
|
||||
Binary file not shown.
181
deployment/ansible/callback_plugins/default_with_clean_msg.py
Normal file
181
deployment/ansible/callback_plugins/default_with_clean_msg.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# Modified to add clean multiline msg formatting
|
||||
#
|
||||
# This plugin extends the default callback with enhanced multiline message formatting.
|
||||
# It suppresses warnings by implementing its own versions of methods that would
|
||||
# otherwise call get_option() before options are initialized.
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: default_with_clean_msg
|
||||
type: stdout
|
||||
short_description: default Ansible screen output with clean multiline messages
|
||||
version_added: historical
|
||||
description:
|
||||
This is the default output callback for ansible-playbook with enhanced
|
||||
multiline message formatting. Multiline messages in msg fields are
|
||||
displayed as clean, readable blocks instead of escaped newline characters.
|
||||
'''
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.plugins.callback.default import CallbackModule as DefaultCallbackModule
|
||||
|
||||
|
||||
class CallbackModule(DefaultCallbackModule):
|
||||
'''
|
||||
This extends the default callback with enhanced multiline message formatting.
|
||||
Multiline messages in 'msg' fields are displayed as clean, readable blocks.
|
||||
'''
|
||||
|
||||
CALLBACK_NAME = 'default_with_clean_msg'
|
||||
|
||||
def _print_clean_msg(self, result, color=C.COLOR_VERBOSE):
|
||||
'''
|
||||
Print multiline messages in a clean, readable format with borders.
|
||||
This makes it easy to read and copy multiline content from debug outputs.
|
||||
'''
|
||||
msg_body = result._result.get('msg')
|
||||
if isinstance(msg_body, str) and msg_body.strip():
|
||||
lines = msg_body.strip().splitlines()
|
||||
if len(lines) > 1: # Only format if multiline
|
||||
max_len = max(len(line) for line in lines if line.strip())
|
||||
if max_len > 0:
|
||||
border = "=" * min(max_len, 80) # Limit border width
|
||||
self._display.display("\n" + border, color=color)
|
||||
for line in lines:
|
||||
self._display.display(line, color=color)
|
||||
self._display.display(border + "\n", color=color)
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
# Suppress warnings by implementing our own version that doesn't call get_option early
|
||||
# Initialize state if needed
|
||||
if not hasattr(self, '_play'):
|
||||
self._play = None
|
||||
if not hasattr(self, '_last_task_banner'):
|
||||
self._last_task_banner = None
|
||||
if not hasattr(self, '_task_type_cache'):
|
||||
self._task_type_cache = {}
|
||||
|
||||
# Cache task prefix
|
||||
self._task_type_cache[task._uuid] = 'TASK'
|
||||
|
||||
# Store task name
|
||||
if self._play and hasattr(self._play, 'strategy'):
|
||||
from ansible.utils.fqcn import add_internal_fqcns
|
||||
if self._play.strategy in add_internal_fqcns(('free', 'host_pinned')):
|
||||
self._last_task_name = None
|
||||
else:
|
||||
self._last_task_name = task.get_name().strip()
|
||||
else:
|
||||
self._last_task_name = task.get_name().strip()
|
||||
|
||||
# Print task banner (only if we should display it)
|
||||
# We skip the parent's check for display_skipped_hosts/display_ok_hosts to avoid warnings
|
||||
if self._play and hasattr(self._play, 'strategy'):
|
||||
from ansible.utils.fqcn import add_internal_fqcns
|
||||
if self._play.strategy not in add_internal_fqcns(('free', 'host_pinned')):
|
||||
self._last_task_banner = task._uuid
|
||||
self._display.banner('TASK [%s]' % task.get_name().strip())
|
||||
|
||||
def v2_runner_on_start(self, host, task):
|
||||
# Suppress warnings by not calling parent if options aren't ready
|
||||
# This method is optional and only shows per-host start messages
|
||||
# We can safely skip it to avoid warnings
|
||||
pass
|
||||
|
||||
def v2_runner_on_ok(self, result):
|
||||
# Suppress warnings by implementing our own version that doesn't call get_option
|
||||
host_label = self.host_label(result)
|
||||
|
||||
# Handle TaskInclude separately
|
||||
from ansible.playbook.task_include import TaskInclude
|
||||
if isinstance(result._task, TaskInclude):
|
||||
if self._last_task_banner != result._task._uuid:
|
||||
self.v2_playbook_on_task_start(result._task, False)
|
||||
return
|
||||
|
||||
# Clean results and handle warnings
|
||||
self._clean_results(result._result, result._task.action)
|
||||
self._handle_warnings(result._result)
|
||||
|
||||
# Handle loop results
|
||||
if result._task.loop and 'results' in result._result:
|
||||
self._process_items(result)
|
||||
return
|
||||
|
||||
# Determine status and color
|
||||
if result._result.get('changed', False):
|
||||
if self._last_task_banner != result._task._uuid:
|
||||
self.v2_playbook_on_task_start(result._task, False)
|
||||
self._display.display("changed: [%s]" % host_label, color=C.COLOR_CHANGED)
|
||||
color = C.COLOR_CHANGED
|
||||
else:
|
||||
# Always display ok hosts (skip get_option check to avoid warnings)
|
||||
if self._last_task_banner != result._task._uuid:
|
||||
self.v2_playbook_on_task_start(result._task, False)
|
||||
self._display.display("ok: [%s]" % host_label, color=C.COLOR_OK)
|
||||
color = C.COLOR_OK
|
||||
|
||||
# Add our clean message formatting
|
||||
self._print_clean_msg(result, color=color)
|
||||
|
||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||
# Suppress warnings by implementing our own version
|
||||
host_label = self.host_label(result)
|
||||
self._clean_results(result._result, result._task.action)
|
||||
|
||||
if self._last_task_banner != result._task._uuid:
|
||||
self.v2_playbook_on_task_start(result._task, False)
|
||||
|
||||
self._handle_exception(result._result, use_stderr=False)
|
||||
self._handle_warnings(result._result)
|
||||
|
||||
if result._task.loop and 'results' in result._result:
|
||||
self._process_items(result)
|
||||
else:
|
||||
msg = "fatal: [%s]: FAILED! => %s" % (host_label, self._dump_results(result._result))
|
||||
self._display.display(msg, color=C.COLOR_ERROR)
|
||||
|
||||
if ignore_errors:
|
||||
self._display.display("...ignoring", color=C.COLOR_SKIP)
|
||||
|
||||
# Add our clean message formatting
|
||||
self._print_clean_msg(result, color=C.COLOR_ERROR)
|
||||
|
||||
def v2_runner_on_skipped(self, result):
|
||||
# Suppress warnings by implementing our own version
|
||||
# Always display skipped hosts (skip get_option check to avoid warnings)
|
||||
self._clean_results(result._result, result._task.action)
|
||||
|
||||
if self._last_task_banner != result._task._uuid:
|
||||
self.v2_playbook_on_task_start(result._task, False)
|
||||
|
||||
if result._task.loop is not None and 'results' in result._result:
|
||||
self._process_items(result)
|
||||
else:
|
||||
msg = "skipping: [%s]" % result._host.get_name()
|
||||
if self._run_is_verbose(result):
|
||||
msg += " => %s" % self._dump_results(result._result)
|
||||
self._display.display(msg, color=C.COLOR_SKIP)
|
||||
|
||||
# Add our clean message formatting
|
||||
self._print_clean_msg(result, color=C.COLOR_SKIP)
|
||||
|
||||
def v2_runner_on_unreachable(self, result):
|
||||
# Suppress warnings by implementing our own version
|
||||
if self._last_task_banner != result._task._uuid:
|
||||
self.v2_playbook_on_task_start(result._task, False)
|
||||
|
||||
host_label = self.host_label(result)
|
||||
msg = "fatal: [%s]: UNREACHABLE! => %s" % (host_label, self._dump_results(result._result))
|
||||
self._display.display(msg, color=C.COLOR_UNREACHABLE)
|
||||
|
||||
if result._task.ignore_unreachable:
|
||||
self._display.display("...ignoring", color=C.COLOR_SKIP)
|
||||
|
||||
# Add our clean message formatting
|
||||
self._print_clean_msg(result, color=C.COLOR_UNREACHABLE)
|
||||
Reference in New Issue
Block a user