Files
computer-playbook/roles/web-app-xwiki/tasks/04_extensions.yml
Kevin Veen-Birkenbach 62493ac5a9 XWiki: increase installer execution timeout and add retries
The task 'XWIKI | Execute installer page' now uses:
- timeout: 300 (allow up to 5 min per request)
- retries: 20
- delay: 15
- until: condition

This prevents early failures during the first Distribution Wizard bootstrap when hundreds of extensions are still being installed.

Context: https://chatgpt.com/share/68ca0f18-2124-800f-a70d-df1811966107
2025-09-17 03:30:40 +02:00

140 lines
5.3 KiB
YAML

# roles/web-app-xwiki/tasks/04_extensions.yml
#
# Installs OIDC / LDAP using a temporary Groovy page that calls the
# Extension Script Service (services.extension.install).
# Avoids REST job API and any Namespace class import for portability.
#
# Flow:
# - Bootstrap config renders with both auth backends OFF (already in your role).
# - This file installs required extensions on the current wiki.
# - Final config later turns auth ON (already in your role).
#
# Notes:
# - We print machine-readable markers so Ansible can assert deterministically.
# - We protect XWiki's {{groovy}} wiki macro from Jinja by using {% raw %}…{% endraw %}.
- name: "XWIKI | Build Groovy installer code (no wiki macro delimiters here)"
set_fact:
_install_code: |
def ext = services.extension
def ns = "wiki:xwiki"
// Build the wish list from Ansible vars
def wanted = []
{% if XWIKI_OIDC_ENABLED | bool %}
wanted << [id: "{{ XWIKI_EXT_OIDC_ID }}", version: "{{ XWIKI_EXT_OIDC_VERSION }}"]
{% endif %}
{% if XWIKI_LDAP_ENABLED | bool %}
wanted << [id: "{{ XWIKI_EXT_LDAP_ID }}", version: "{{ XWIKI_EXT_LDAP_VERSION }}"]
{% endif %}
if (wanted.isEmpty()) {
println "SKIP: no extensions requested"
} else {
wanted.each { e ->
def already = ext.getInstalledExtension(e.id as String, ns)
if (already) {
println "ALREADY_INSTALLED::${e.id}::${already.id?.version}"
} else {
println "INSTALL_START::${e.id}::${e.version}"
def job = ext.install(e.id as String, e.version as String, ns)
// Heartbeat until terminal state
long last = System.currentTimeMillis()
while (true) {
def st = job?.status?.state
if (st && st.name() in ["FINISHED","FAILED","CANCELED"]) {
println "STATE=${st.name()}::${e.id}"
break
}
if (System.currentTimeMillis() - last > 2000) {
println "STATE=" + (st?.name() ?: "PENDING") + "::" + e.id
last = System.currentTimeMillis()
}
Thread.sleep(500)
}
// Verify presence after job completion
def now = ext.getInstalledExtension(e.id as String, ns)
if (now) {
println "INSTALLED_OK::${e.id}::${now.id?.version}"
} else {
println "INSTALLED_MISSING::${e.id}"
}
}
}
}
- name: "XWIKI | PUT installer page Main.InstallExtensions"
uri:
url: "{{ [XWIKI_REST_XWIKI_PAGES, 'InstallExtensions'] | url_join }}"
method: PUT
user: "{{ XWIKI_SUPERADMIN_USERNAME }}"
password: "{{ XWIKI_SUPERADMIN_PASSWORD }}"
force_basic_auth: true
status_code: [200, 201, 202, 204]
headers:
Content-Type: "application/xml"
Accept: "application/xml"
body: |
<page xmlns="http://www.xwiki.org">
<title>InstallExtensions</title>
<content><![CDATA[
{% raw %}{{groovy}}{% endraw %}
{{ _install_code | indent(8, False) }}
{% raw %}{{/groovy}}{% endraw %}
]]></content>
<syntax>xwiki/2.1</syntax>
</page>
register: _put_page
- name: "XWIKI | Execute installer page"
uri:
url: "http://127.0.0.1:{{ XWIKI_HOST_PORT }}/bin/view/XWiki/InstallExtensions?xpage=plain"
method: GET
user: "{{ XWIKI_SUPERADMIN_USERNAME }}"
password: "{{ XWIKI_SUPERADMIN_PASSWORD }}"
force_basic_auth: true
status_code: [200]
return_content: yes
timeout: 300 # allow up to 5 minutes per attempt
register: _exec_page
retries: 20 # retry up to 20 times
delay: 15 # wait 15 seconds between retries
until: _exec_page is succeeded
# Assert success:
# - If nothing was requested, allow "SKIP: no extensions requested".
# - For requested OIDC/LDAP, require ALREADY_INSTALLED or INSTALLED_OK.
# - Disallow INSTALLED_MISSING.
- name: "ASSERT | Extension installation markers"
vars:
_c: "{{ _exec_page.content | default('') }}"
_need_oidc: "{{ XWIKI_OIDC_ENABLED | bool }}"
_need_ldap: "{{ XWIKI_LDAP_ENABLED | bool }}"
_ok_oidc: "{{ (_c is search('ALREADY_INSTALLED::' ~ XWIKI_EXT_OIDC_ID)) or (_c is search('INSTALLED_OK::' ~ XWIKI_EXT_OIDC_ID)) }}"
_ok_ldap: "{{ (_c is search('ALREADY_INSTALLED::' ~ XWIKI_EXT_LDAP_ID)) or (_c is search('INSTALLED_OK::' ~ XWIKI_EXT_LDAP_ID)) }}"
_miss_any: "{{ _c is search('INSTALLED_MISSING::') }}"
_skip_all: "{{ _c is search('SKIP: no extensions requested') }}"
assert:
that:
- _miss_any | bool == false
- (_need_oidc and _ok_oidc) or (not _need_oidc)
- (_need_ldap and _ok_ldap) or (not _need_ldap)
- (_need_oidc or _need_ldap) or _skip_all
fail_msg: >-
Extension install did not complete successfully.
Output was:
{{ (_exec_page.content | default('') | regex_replace('\\s+', ' ') | truncate(1000)) }}
- name: "XWIKI | Delete installer page"
uri:
url: "{{ [XWIKI_REST_XWIKI_PAGES, 'InstallExtensions'] | url_join }}"
method: DELETE
user: "{{ XWIKI_SUPERADMIN_USERNAME }}"
password: "{{ XWIKI_SUPERADMIN_PASSWORD }}"
force_basic_auth: true
status_code: [204, 200, 202, 404]
register: _delete_page
changed_when: _delete_page.status != 404