Feat/numishare tomcat#237
Open
danyalberchtoldlf wants to merge 9 commits intomainfrom
Open
Conversation
…urityManager, allowPaths
* Add Tomcat-9-style RHEL 10 support: pick java-21-openjdk-headless via a new
vars/RedHat10.yml override (java-17 was dropped from EL10 AppStream). EL8/9
are unchanged.
* Switch the Solr 9.x tarball download to dlcdn.apache.org first, fall back
to archive.apache.org on 404 / failure. The archive server advertises
`Vary: Slow,Glacial` and was making get_url hang for ~30 minutes; the CDN
delivers a 371 MB tarball in seconds. Solr 8.x stays on the Lucene archive
path (EOL, no longer mirrored by the CDN).
* Add `become: false` to both `delegate_to: localhost` get_url tasks so the
role works under ansible-navigator EEs (sudo without a password fails
inside the EE container).
* Expose three previously-buried-or-missing knobs as user-facing variables:
- apache_solr__heap (default '512m', drives SOLR_HEAP)
- apache_solr__security_manager_enabled (default true; disable for solr.solr.home outside Solr's permitted paths, e.g. Numishare)
- apache_solr__allow_paths (default []; auto-augmented with apache_solr__dump_directory when backups are enabled, so Solr 9 can write its snapshots)
* Add a mariadb-dump-style backup pipeline:
- /usr/local/bin/apache-solr-dump (curl + replication?command=backup,
polls command=details until status==success, status==exception fails
loudly, status==unknown after 10 min per core fails loudly)
- /etc/apache-solr-dump.conf (sourced by the script)
- apache-solr-dump.service (Type=oneshot, After=solr.service)
- apache-solr-dump.timer (default 22:<minute-by-host-seed>:00 daily)
Wipe-and-refresh per run; retention is the surrounding backup tool's job.
apache_solr__dump_cores empty (default) disables the timer. The dump
directory's parent is created as 0o755 root:root so other dump pipelines
(existdb-dump, mariadb-dump) sharing /backup/ can still traverse into
their own subdirs.
* Mark apache_solr as proven on RHEL 10 in COMPATIBILITY.md.
* README documents the optional backup variables and the matching restore
via replication?command=restore + restorestatus polling.
…_xms/xmx/xx defaults
* Clone the existing 9.0 templates to a 10.1 set:
etc/tomcat/10.1-server.xml.j2
etc/tomcat/10.1-context.xml.j2
etc/tomcat/10.1-logging.properties.j2
etc/tomcat/10.1-tomcat-users.xml.j2
etc/sysconfig/10.1-tomcat.j2
The role's `tomcat__installed_version` lookup picks them up automatically
on RHEL 10 (which ships tomcat-10.1.x in AppStream). The cloned configs
are byte-compatible enough for our use case; once concrete divergences
surface they will be patched in place rather than re-derived from the
vendor stock files. EL8/9 deployments using 9.0 are unchanged.
* Expose the JVM heap knobs that lived as inline `| d('1024M')` fallbacks
inside the sysconfig templates as first-class defaults:
apache_tomcat__env_xms (default '1024M')
apache_tomcat__env_xmx (default '1024M')
apache_tomcat__env_xx (default '+UseParallelGC')
User-visible behavior is unchanged; users who want to tune the Tomcat
heap from their inventory now have documented variables. Both the 9.0
and 10.1 sysconfig templates drop the inline `| d(...)` and reference
the defaults directly.
* Mark apache_tomcat as proven on RHEL 10 in COMPATIBILITY.md.
New role to install and operate eXist-db 6.x as a systemd-managed service. Targeted at the Numishare stack but standalone-usable. Install: * Extract upstream exist-distribution-<version>-unix.tar.bz2 to /opt/existdb * Create the existdb system user and group * Create /var/lib/existdb/data and /var/log/existdb with the right ownership * Rewrite Jetty HTTP/HTTPS ports off 8080/8443 to existdb__http_port (8888) / existdb__https_port (8444) so eXist-db can coexist with Tomcat or Wildfly on the same host * Redirect log4j2 output to existdb__log_dir (/var/log/existdb) * Force client.properties to 127.0.0.1 — eXist-db's Jetty binds IPv4 only, so on dual-stack hosts the bundled CLI tools (client.sh, backup.sh, restore.sh) hit the obscure "HTTP server returned unexpected status: null" when localhost resolves to ::1 first * Set the admin password on first install only, gated by a marker file (.linuxfabrik-admin-password-set) so re-runs don't clobber a manually rotated password * Drop a systemd unit, daemon-reload on changes, enable & start Backup pipeline (mariadb-dump-style): * /usr/local/bin/existdb-dump (calls bin/backup.sh; uses runuser instead of sudo because systemd's no-TTY oneshot context swallows sudo's stderr; passes -ouri=xmldb:exist://127.0.0.1:<port>/exist/xmlrpc explicitly because bin/backup.sh ignores client.properties at runtime and falls back to the compiled-in default port 8080) * /etc/existdb-dump.conf (sourced by the script) * existdb-dump.service (Type=oneshot, After=existdb.service) * existdb-dump.timer (default 22:<minute-by-host-seed>:00 daily) * Wipe-and-refresh per run; retention is the surrounding backup tool's job * Toggle via existdb__dump_enabled (default true) * Dump directory's parent is created as 0o755 root:root so other dump pipelines (apache-solr-dump, mariadb-dump) sharing /backup/ can still traverse into their own subdirs README documents both the role and the matching bin/restore.sh invocation for disaster recovery. COMPATIBILITY: marked as proven on RHEL 10.
New role for Numishare (https://github.com/ewg118/numishare), the open-source numismatic-collection platform. The role wires Numishare into an already-deployed eXist-db / Solr / Tomcat / Orbeon stack rather than managing those services itself. Install: * Install git-core and shallow-clone numishare__git_url to numishare__install_dir (default /opt/numishare). update: false — the checkout is one-time, intentional updates happen out-of-band. * Deploy /opt/numishare/exist-config.xml so Numishare's XPL pipelines know how to authenticate against eXist-db. Mode 0640 (contains the eXist-db admin password in plaintext) and owned by the Tomcat user/group via numishare__app_{user,group}. * Deploy /opt/numishare/solr-home/<version>/core.properties and create the /var/solr/data/<core_name> -> /opt/numishare/solr-home/<version> symlink so Solr's core discovery picks up the Numishare core. Both notify the apache_solr restart handler so Solr reloads the core (Solr only scans for cores at startup, so without the notify the core would never load on first deploy). * chown -R numishare__solr_user:numishare__solr_group on the solr-home subtree. Themes: * mkdir -p numishare__themes_dir (default /opt/themes). * Symlink <themes_dir>/default -> <install_dir>/ui to expose Numishare's bundled UI as the "default" theme via the same /orbeon/themes/<name>/ delivery path as custom themes. * Deploy custom themes from inventory via the numishare__themes__* combined-var pattern. Each entry is keyed by `name`; source is either git_url (with optional git_version, git_update) or tarball_url (with optional tarball_strip_components). state: 'absent' removes the theme. README documents the variables and gives examples for both sources. COMPATIBILITY: marked as proven on RHEL 10.
New role to deploy Orbeon Forms (https://www.orbeon.com/) Community Edition into an already-deployed Tomcat for the Numishare stack. Heavily wired to Numishare's expectations rather than a generic Orbeon installer. Deployment: * Install unzip, fetch orbeon_forms__zip_url, extract orbeon.war from the CE zip, and unarchive it exploded into orbeon_forms__home (/var/lib/tomcat/webapps/orbeon). * Deploy <tomcat-conf>/Catalina/localhost/orbeon.xml with <Resources allowLinking="true"/>. The `path` attribute is intentionally omitted — Tomcat ignores it for context descriptors and emits a warning during startup. Log path fix (RHEL 10 Tomcat): * Orbeon's bundled WEB-INF/resources/config/log4j2.xml references log files via the relative path `../logs/orbeon.log`. With CATALINA_BASE on /usr/share/tomcat (RHEL 10), this resolves to /usr/share/logs/, which the tomcat user cannot create. Every FileAppender silently fails to initialize, Orbeon's diagnostics disappear, and the only surface is a generic "Page Not Found" with no log trail. The role rewrites the prefix to orbeon_forms__log_dir (default /var/log/tomcat). Numishare wiring: * Symlink orbeon_forms__numishare_dir -> WEB-INF/resources/apps/numishare. * Copy <numishare>/vendor/exist-xqj-api-1.0.1/*.jar into WEB-INF/lib/. * Place a Numishare-branded favicon at WEB-INF/resources/ops/images/ orbeon-icon-16.{ico,png}. Numishare's xforms templates hardcode that path; Orbeon 2023.1 removed the file from the WAR (the entire WEB-INF/resources/ops/ tree is gone), so without this every Numishare page emits a 404 for the favicon. * Copy properties-local.xml.template -> properties-local.xml (idempotent via force: false) and inject a Numishare block via blockinfile markers (oxf.epilogue.theme, oxf.fr.authentication.method=container, container roles). * Replace the shipped <login-config> with the configured BASIC or FORM auth block (orbeon_forms__auth_method). * Replace the shipped <session-config> with orbeon_forms__session_timeout (default 720 minutes; Numishare's admin forms benefit from a longer timeout). * Inject a Numishare block before </web-app> (security-constraint for /numishare/admin/*, security-roles, /themes/* servlet-mapping for the Tomcat default servlet). Themes: * Symlink orbeon_forms__themes_dir (/opt/themes) into both the discovery path (apps/themes) and the delivery path (orbeon_home/themes), matching the structure the numishare role's apps/themes/default symlink expects. Final chown -R orbeon_forms__tomcat_user:orbeon_forms__tomcat_group on the deployment, then unconditional `systemctl restart tomcat.service`. orbeon_forms__roles default ships only `numishare-admin` (the universal Numishare role); per-collection container roles are added via the inventory. COMPATIBILITY: marked as proven on RHEL 10.
New setup_* playbook that wires up the full Numishare stack on a single
host, in the correct order:
1. linuxfabrik.lfops.apps (OS-level deps; injects apache_solr's
bc / lsof / pwgen / tar via
apache_solr__apps__apps__dependent_var)
2. linuxfabrik.lfops.apache_solr (numishare's solr-home is outside
Solr's permitted paths, so we inject
apache_solr__security_manager_enabled:
false and apache_solr__dump_cores:
['numishare'] for the backup pipeline)
3. linuxfabrik.lfops.apache_tomcat (provides the tomcat user/group
numishare and orbeon_forms chown to)
4. linuxfabrik.lfops.numishare (writes the eXist-config and Solr
core wiring before existdb/orbeon
reference them)
5. linuxfabrik.lfops.existdb (XML database; numishare's
exist-config.xml points at it)
6. linuxfabrik.lfops.orbeon_forms (final WAR deployment + Numishare
properties / web.xml / themes)
Each role can be skipped via setup_numishare__<role>__skip_role (per the
lfops setup_* convention). pre_tasks / post_tasks log start and end via
the shared role's log-start.yml / log-end.yml so a run leaves a trail in
/var/log/linuxfabrik-lfops.log.
…eon_forms CONTRIBUTING.md requires every role to ship `meta/argument_specs.yml` declaring all user-facing variables with types and (where applicable) defaults, so Ansible validates them at role entry without manual `assert + is defined` blocks in the tasks. Each spec covers what is documented in the role README: * mandatory variables (none of these three roles have any — the eXist-db admin password defaults to a non-secure placeholder and is documented as "must be overridden") * simple optional variables with their static defaults * the __host_var / __group_var halves of injection variables (numishare__themes__*) — internal __role_var, __dependent_var and __combined_var are intentionally excluded per CONTRIBUTING. `default` is omitted on entries whose `defaults/main.yml` value is a Jinja2 expression that argument_specs cannot evaluate (existdb__dump_on_calendar, existdb__dump_password). orbeon_forms__auth_method ships an explicit `choices: ['BASIC', 'FORM']` constraint matching the README documentation; `BASIC` and `FORM` are the only auth methods the login-config.xml.j2 template emits.
…ctions, service split, playbooks docs)
* playbooks/README.md: document setup_numishare.yml in alphabetical
position between setup_nextcloud and setup_rocketchat (CONTRIBUTING:
"After creating a new playbook, document it in playbooks/README.md").
* playbooks/all.yml: import setup_numishare.yml in the alphabetical slot
between setup_nextcloud.yml and setup_rocketchat.yml (CONTRIBUTING: "and
add it in the playbooks/all.yml").
* playbooks/setup_numishare.yml: introduce the
setup_numishare__apache_solr__skip_injections__internal_var pattern
exactly as CONTRIBUTING.md spells it out for setup_* playbooks. Defaults
to the apache_solr skip_role state, can be overridden via
setup_numishare__apache_solr__skip_injections to allow running the
apache_solr role without injecting its OS deps into the apps role (e.g.
when the user manages OS deps elsewhere). Apply via ternary on the
apps role's apps__apps__dependent_var.
* roles/existdb/tasks/main.yml: split the combined enable+state systemd
call into two separate ansible.builtin.service tasks per CONTRIBUTING
("Split the service `enabled` and `state` into separate tasks. This is
relevant for handlers that would restart the service"). Same split for
existdb-dump.timer; the timer block now also has its own
ansible.builtin.systemd `daemon_reload: true` task gated on the
template-deploy result rather than piggybacking on the combined call.
Register the existdb.service state task as
__existdb__service_state_result for any future handler that needs to
skip a redundant restart, matching the pattern in roles/example.
… paths to numishare__install_dir Numishare upstream ships two files with the install path hardcoded to `/usr/local/projects/numishare` — the upstream maintainer's own layout: * `xforms/admin.xhtml` line 101: the `<installation_path>` element in the XForms instance for the Add-New-Collection form. The value pre-fills the "Installation Directory" field and gets persisted into eXist-db per collection at `/db/numishare/<collection>/config.xml`. * `script/reindex-collection.php`: the `$eXist_config_path` constant points at `/usr/local/projects/numishare/exist-config.xml` for the CLI reindex helper. lfops installs Numishare to `/opt/numishare` by default. Without this rewrite the admin form defaults to a non-existent path on every fresh install (so the user has to clear and retype the field on every new collection), and the reindex script crashes on missing `exist-config.xml` until it is manually patched. Add an `ansible.builtin.replace` task right after the git clone, scoped to the two known files via a loop. We do not regex over the whole checkout to avoid accidentally rewriting `docker/docker-compose.yml`, which uses `/usr/local/projects/numishare` as a Docker volume mount path that is intentionally independent of the host layout. Re-runs are idempotent: replace finds nothing to change after the first pass since the literal `/usr/local/projects/numishare` is gone.
NavidSassan
requested changes
Apr 29, 2026
| @@ -0,0 +1,78 @@ | |||
| - name: 'Playbook linuxfabrik.lfops.setup_numishare' | |||
| hosts: 'all' | |||
Member
There was a problem hiding this comment.
this needs to be lfops_setup_numishare
Comment on lines
+3
to
+5
| become: true | ||
| any_errors_fatal: true | ||
| serial: 1 |
Member
There was a problem hiding this comment.
don't set these unless absolutely necessary
Comment on lines
+27
to
+36
| # OS-level helpers (java, git, unzip, ...). Reduce to whatever is missing | ||
| # by overriding `apps__apps__host_var` in the inventory. apache_solr's OS | ||
| # dependencies (bc, lsof, pwgen, tar) are injected here unless the user | ||
| # opts out via setup_numishare__apache_solr__skip_injections. | ||
| - role: 'linuxfabrik.lfops.apps' | ||
| apps__apps__dependent_var: '{{ | ||
| (not setup_numishare__apache_solr__skip_injections__internal_var) | ternary(apache_solr__apps__apps__dependent_var, []) | ||
| }}' | ||
| when: | ||
| - 'not setup_numishare__apps__skip_role__internal_var' |
Member
There was a problem hiding this comment.
just do this directly in the role itself
| @@ -1,18 +1,29 @@ | |||
| apache_solr__allow_paths: [] # extra paths Solr is allowed to read/write (-Dsolr.allowPaths). Backup directory is auto-included when apache_solr__dump_cores is non-empty. | |||
Member
There was a problem hiding this comment.
comments in the source code should be intended for the developers. user-facing info belongs into the README
| dest: '/tmp/solr-{{ apache_solr__version }}.tgz' | ||
| checksum: '{{ apache_solr__checksum }}' | ||
| delegate_to: 'localhost' | ||
| become: false # writes to /tmp/ on the controller; ansible-navigator EE has no sudo |
Member
There was a problem hiding this comment.
don't set this in the playbooks / roles, it should be set in the inventory for the respective host
| @@ -0,0 +1,14 @@ | |||
| <!-- {{ ansible_managed }} --> | |||
| @@ -0,0 +1,10 @@ | |||
| <!-- ANSIBLE MANAGED: Numishare login-config --> | |||
Member
There was a problem hiding this comment.
wrong header & missing timestamp
| @@ -0,0 +1,25 @@ | |||
| <!-- Numishare specific configuration --> | |||
Comment on lines
+34
to
+36
| ## Mandatory Role Variables | ||
|
|
||
| None. All variables have defaults that match the Numishare stack layout. |
| * Direct download URL of the Orbeon CE zip. The role unpacks `orbeon.war` from inside the zip and explodes it. | ||
| * Type: String. | ||
| * Default: `'https://github.com/orbeon/orbeon-forms/releases/download/tag-release-2023.1-ce/orbeon-2023.1.202312312000-CE.zip'` | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.