mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-10-31 18:29:21 +00:00 
			
		
		
		
	feat(web-app-xwiki): install OIDC/LDAP via temporary Groovy page (PUT→execute→verify→delete)
Replace REST jobs flow with services.extension.install executed from a transient XWiki.InstallExtensions page. - Build wishlist from Ansible vars; print machine-readable markers; assert success. - Execute from XWiki space; delete page afterwards; fix delete changed_when. - Use Jinja raw + indent for clean macro embedding. https://chatgpt.com/share/68c9ebf5-f5e0-800f-9b80-372b4b31e772
This commit is contained in:
		| @@ -1,152 +1,135 @@ | ||||
| # roles/web-app-xwiki/tasks/04_extensions.yml | ||||
| # | ||||
| # Canonical install flow for XWiki 16.10.x in this setup: | ||||
| # - PUT /rest/jobs with query params (install, async=false, media=json, namespaces, extensions) | ||||
| # - Pass lists via <rest><list>…</list></rest> in query parameters | ||||
| # - Minimal XML body (<jobRequest/>) just to satisfy the REST layer | ||||
| # - Restart once (handler: docker compose restart), then verify via the 'installed' repository | ||||
| # 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. | ||||
| # | ||||
| # Requires in xwiki.properties: | ||||
| # extension.repositories=xwiki-public:maven:https://nexus.xwiki.org/nexus/content/groups/public/,central:maven:https://repo1.maven.org/maven2/ | ||||
| # 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 %}. | ||||
|  | ||||
| # 1) Probe what is already installed | ||||
| - name: "Probe OIDC extension" | ||||
|   include_tasks: _probe_extension.yml | ||||
|   vars: | ||||
|     ext_id: "{{ XWIKI_EXT_OIDC_ID }}" | ||||
|     ext_version: "{{ XWIKI_EXT_OIDC_VERSION }}" | ||||
|     ext_enabled: "{{ XWIKI_OIDC_ENABLED }}" | ||||
|     result_var: "xwiki_oidc_ext" | ||||
|  | ||||
| - name: "Probe LDAP extension" | ||||
|   include_tasks: _probe_extension.yml | ||||
|   vars: | ||||
|     ext_id: "{{ XWIKI_EXT_LDAP_ID }}" | ||||
|     ext_version: "{{ XWIKI_EXT_LDAP_VERSION }}" | ||||
|     ext_enabled: "{{ XWIKI_LDAP_ENABLED }}" | ||||
|     result_var: "xwiki_ldap_ext" | ||||
|  | ||||
| # 2) Build the list of extensions to install (enabled + missing) | ||||
| - name: "Initialize extension install list" | ||||
| - name: "XWIKI | Build Groovy installer code (no wiki macro delimiters here)" | ||||
|   set_fact: | ||||
|     extensions_to_install: [] | ||||
|     _install_code: | | ||||
|       def ext = services.extension | ||||
|       def ns  = "wiki:xwiki" | ||||
|  | ||||
| - name: "Queue OIDC extension for install" | ||||
|   when: | ||||
|     - XWIKI_OIDC_ENABLED | bool | ||||
|     - xwiki_oidc_ext.status | int != 200 | ||||
|   set_fact: | ||||
|     extensions_to_install: "{{ extensions_to_install + [ {'id': XWIKI_EXT_OIDC_ID, 'version': XWIKI_EXT_OIDC_VERSION} ] }}" | ||||
|       // 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 %} | ||||
|  | ||||
| - name: "Queue LDAP extension for install" | ||||
|   when: | ||||
|     - XWIKI_LDAP_ENABLED | bool | ||||
|     - xwiki_ldap_ext is defined | ||||
|     - xwiki_ldap_ext.status | int != 200 | ||||
|   set_fact: | ||||
|     extensions_to_install: "{{ extensions_to_install + [ {'id': XWIKI_EXT_LDAP_ID, 'version': XWIKI_EXT_LDAP_VERSION} ] }}" | ||||
|       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) | ||||
|  | ||||
| # 3) Build XML payloads for the request BODY (not query params) | ||||
| - name: "Build XML payloads for job body" | ||||
|   when: extensions_to_install | length > 0 | ||||
|   set_fact: | ||||
|     namespaces_xml_rest: "<rest><list><string>wiki:xwiki</string></list></rest>" | ||||
|     extensions_xml_rest: >- | ||||
|       <rest><list>{% for ext in extensions_to_install -%} | ||||
|       <extension><id>{{ ext.id }}</id><version>{{ ext.version }}</version><namespace>wiki:xwiki</namespace></extension> | ||||
|       {%- endfor %}</list></rest> | ||||
|             // 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) | ||||
|             } | ||||
|  | ||||
| # 4) Install extensions synchronously using BODY properties | ||||
| - name: "Install extensions via PUT (body properties, sync)" | ||||
|   when: extensions_to_install | length > 0 | ||||
|             // 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_EXTENSION_INSTALL }}?jobType=install&async=false&media=json" | ||||
|     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: | ||||
|       Accept: "application/json" | ||||
|       Content-Type: "application/xml" | ||||
|       Accept: "application/xml" | ||||
|     body: | | ||||
|       <jobRequest xmlns="http://www.xwiki.org"> | ||||
|         <request> | ||||
|           <properties> | ||||
|             <property> | ||||
|               <key>namespaces</key> | ||||
|               <value><![CDATA[{{ namespaces_xml_rest }}]]></value> | ||||
|             </property> | ||||
|             <property> | ||||
|               <key>extensions</key> | ||||
|               <value><![CDATA[{{ extensions_xml_rest }}]]></value> | ||||
|             </property> | ||||
|             <property> | ||||
|               <key>interactive</key> | ||||
|               <value><rest><boolean>false</boolean></rest></value> | ||||
|             </property> | ||||
|           </properties> | ||||
|         </request> | ||||
|       </jobRequest> | ||||
|     status_code: [200, 201, 202] | ||||
|     return_content: yes | ||||
|   register: xwiki_install_job | ||||
|   notify: docker compose restart | ||||
|   changed_when: xwiki_install_job.json.state == 'FINISHED' | ||||
|       <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 | ||||
|  | ||||
| # 5) Restart once so new classes are loaded | ||||
| - name: "Restart XWiki after install" | ||||
|   when: extensions_to_install | length > 0 | ||||
|   meta: flush_handlers | ||||
|  | ||||
| # 6) Wait for REST to be ready again | ||||
| - name: "Wait until XWiki REST is ready after restart" | ||||
| - name: "XWIKI | Execute installer page" | ||||
|   uri: | ||||
|     url: "{{ XWIKI_REST_BASE }}" | ||||
|     status_code: [200, 401] | ||||
|     follow_redirects: none | ||||
|     return_content: no | ||||
|   register: xwiki_rest_up_after | ||||
|   changed_when: false | ||||
|   retries: 60 | ||||
|   delay: 5 | ||||
|   until: | ||||
|     - xwiki_rest_up_after is succeeded | ||||
|     - (xwiki_rest_up_after.redirected is not defined) or (not xwiki_rest_up_after.redirected) | ||||
|     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 | ||||
|   register: _exec_page | ||||
|  | ||||
| # 7) Re-probe from 'installed' repository | ||||
| - name: "Re-probe OIDC extension (installed repo)" | ||||
|   include_tasks: _probe_extension.yml | ||||
| # 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: | ||||
|     ext_id: "{{ XWIKI_EXT_OIDC_ID }}" | ||||
|     ext_version: "{{ XWIKI_EXT_OIDC_VERSION }}" | ||||
|     ext_enabled: "{{ XWIKI_OIDC_ENABLED }}" | ||||
|     result_var: "xwiki_oidc_ext_after" | ||||
|     _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: "Re-probe LDAP extension (installed repo)" | ||||
|   include_tasks: _probe_extension.yml | ||||
|   vars: | ||||
|     ext_id: "{{ XWIKI_EXT_LDAP_ID }}" | ||||
|     ext_version: "{{ XWIKI_EXT_LDAP_VERSION }}" | ||||
|     ext_enabled: "{{ XWIKI_LDAP_ENABLED }}" | ||||
|     result_var: "xwiki_ldap_ext_after" | ||||
|  | ||||
| # 8) Fail fast if something requested is still missing | ||||
| - name: "FAIL: OIDC missing after install" | ||||
|   fail: | ||||
|     msg: >- | ||||
|       OIDC extension ({{ XWIKI_EXT_OIDC_ID }} {{ XWIKI_EXT_OIDC_VERSION }}) is NOT detected | ||||
|       after install (HTTP {{ xwiki_oidc_ext_after.status | default('n/a') }}). | ||||
|       Check 'extension.repositories' in xwiki.properties and network/DNS. | ||||
|   when: | ||||
|     - XWIKI_OIDC_ENABLED | bool | ||||
|     - xwiki_oidc_ext_after.status | int != 200 | ||||
|  | ||||
| - name: "FAIL: LDAP missing after install" | ||||
|   fail: | ||||
|     msg: >- | ||||
|       LDAP extension ({{ XWIKI_EXT_LDAP_ID }} {{ XWIKI_EXT_LDAP_VERSION }}) is NOT detected | ||||
|       after install (HTTP {{ xwiki_ldap_ext_after.status | default('n/a') }}). | ||||
|       Check 'extension.repositories' in xwiki.properties and network/DNS. | ||||
|   when: | ||||
|     - XWIKI_LDAP_ENABLED | bool | ||||
|     - xwiki_ldap_ext_after.status | int != 200 | ||||
| - 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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user