From a2f315f959f5b91779b4dca547adc6474d77fdc2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 7 Nov 2025 04:17:23 -0600 Subject: [PATCH] Ensure plugin installation works on Cloudron --- .env.template | 4 ++ CloudronManifest.json | 8 +-- Dockerfile | 7 +-- INSTALL.md | 13 ++++- README.md | 19 +++++++- start.sh | 110 ++++++++++++++++++++++++++++++++++-------- 6 files changed, 129 insertions(+), 32 deletions(-) diff --git a/.env.template b/.env.template index 325288b..a2ed9ee 100644 --- a/.env.template +++ b/.env.template @@ -4,6 +4,10 @@ # Java memory settings - leave empty to auto-configure based on container limits ES_JAVA_HEAP= +# Comma or space separated list of plugins to install automatically +# analysis-icu is required for Nextcloud full-text search with language analyzers +ES_PLUGINS_INSTALL=analysis-icu + # Security settings - DO NOT CHANGE ES_JAVA_HOME=/app/data/jdk ES_PATH_CONF=/app/data/config diff --git a/CloudronManifest.json b/CloudronManifest.json index 456878b..d9bf1a3 100644 --- a/CloudronManifest.json +++ b/CloudronManifest.json @@ -4,7 +4,7 @@ "author": "Elastic and Cloudron Community", "description": "Elasticsearch is a distributed, open source search and analytics engine for all types of data. This package is designed for internal use only.", "tagline": "Distributed search and analytics engine", - "version": "1.0.0", + "version": "1.0.7", "healthCheckPath": "/_cluster/health?pretty", "httpPort": 9200, "manifestVersion": 2, @@ -17,10 +17,6 @@ "localDir": "/data" } }, - "accessRestriction": { - "users": true, - "defaultValue": "internal" - }, "tags": [ "elasticsearch", "search", @@ -37,4 +33,4 @@ "description": "Elasticsearch transport port for node-to-node communication" } } -} \ No newline at end of file +} diff --git a/Dockerfile b/Dockerfile index 2693a13..7bcaf3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,11 +16,11 @@ RUN apt-get update && \ net-tools \ iputils-ping \ dnsutils \ - openjdk-17-jdk + openjdk-21-jdk # Set Elasticsearch version ENV ELASTIC_VERSION=9.1.5 -ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 +ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 # Create elasticsearch user and group RUN groupadd elasticsearch && \ @@ -39,6 +39,7 @@ RUN mkdir -p /app/data/{elasticsearch,logs,config,secrets,jdk/bin,run} && \ # Copy configuration files COPY elasticsearch.yml /app/elasticsearch.yml +COPY .env.template /app/.env.template COPY start.sh /app/start.sh COPY stop.sh /app/stop.sh @@ -50,4 +51,4 @@ HEALTHCHECK --interval=15s --timeout=10s --start-period=120s --retries=5 \ CMD curl -fs -u elastic:$(cat /app/data/secrets/elastic_password) http://localhost:9200/_cluster/health?pretty || exit 1 # Command to run -CMD ["/app/start.sh"] \ No newline at end of file +CMD ["/app/start.sh"] diff --git a/INSTALL.md b/INSTALL.md index d2c42ce..02662dd 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -32,6 +32,17 @@ After installation: 1. Check the app logs to ensure Elasticsearch has started correctly 2. Note the generated password from the logs or from `/app/data/credentials.txt` 3. Configure your other Cloudron apps to connect to Elasticsearch using the format: `http://elastic:@localhost:9200` +4. (Recommended) Ensure the required analysis plugins are installed before integrating apps like Nextcloud: + 1. Open the Cloudron File Manager for the Elasticsearch app and edit `/app/data/.env` + 2. Set `ES_PLUGINS_INSTALL="analysis-icu"` (add extra plugins separated by spaces or commas) + 3. Restart the Elasticsearch app so it installs the requested plugins on startup + 4. Verify installation from the web terminal: + + ```bash + curl -X GET -u elastic: "localhost:9200/_nodes/plugins?pretty" + ``` + + You should see `analysis-icu` listed before running any index commands. ## Troubleshooting @@ -174,4 +185,4 @@ If successful, you should see a JSON response with Elasticsearch information. #### Advanced Configuration -In some cases, you might need to modify additional Elasticsearch settings. You can do this via the elasticsearch.yml file, which is stored in `/app/data/config/elasticsearch.yml` within the Elasticsearch app container. \ No newline at end of file +In some cases, you might need to modify additional Elasticsearch settings. You can do this via the elasticsearch.yml file, which is stored in `/app/data/config/elasticsearch.yml` within the Elasticsearch app container. diff --git a/README.md b/README.md index ced604a..2554898 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This package provides Elasticsearch for Cloudron, configured for internal use on - Elasticsearch 9.1.5 - Single-node configuration optimized for Cloudron - Security enabled with basic authentication +- Automatically installs the `analysis-icu` plugin (configurable) - Internal access only by default (not publicly exposed) - Automatic optimization based on container resources @@ -69,6 +70,22 @@ curl -X PUT "http://elastic:PASSWORD@IPADDRESS:9200/nextcloud" -H 'Content-Type: You can get the IP address from the Cloudron admin panel or by using the `cloudron status` command. The password is stored in `/app/data/credentials.txt`. +### Language Analysis Plugins + +Many integrations (for example, Nextcloud Full-Text Search with German documents) require the `analysis-icu` plugin so Elasticsearch understands language-specific analyzers. This package installs `analysis-icu` automatically on every start. To add additional plugins, edit `/app/data/.env` via the Cloudron File Manager and tweak the `ES_PLUGINS_INSTALL` variable: + +``` +ES_PLUGINS_INSTALL="analysis-icu ingest-attachment" +``` + +Plugins are installed sequentially and skipped if already present. After restarting the app you can verify the installed plugins from the Elasticsearch web terminal: + +```bash +curl -X GET -u elastic: "localhost:9200/_nodes/plugins?pretty" +``` + +Look for `analysis-icu` (and any other requested plugins) in the output before running `occ fulltextsearch:index`. + ## Security Notes - The app is configured as internal-only by default, so it's not exposed to the public internet @@ -94,4 +111,4 @@ The package automatically configures Elasticsearch based on the container's avai ## Support -For support, please create an issue on the package's GitHub repository or contact the package maintainer. \ No newline at end of file +For support, please create an issue on the package's GitHub repository or contact the package maintainer. diff --git a/start.sh b/start.sh index 7052ed6..0a17f3d 100644 --- a/start.sh +++ b/start.sh @@ -56,21 +56,34 @@ setup_password() { # Set up Java environment setup_java() { - if [ -L /app/data/jdk/bin/java ]; then + mkdir -p /app/data/jdk/bin + + if [ -x /app/data/jdk/bin/java ]; then + echo "Java already configured: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)" return 0 fi echo "Setting up Java environment..." - # Try Java 17 first, then fall back to system Java - if [ -f /usr/lib/jvm/java-17-openjdk-amd64/bin/java ]; then - ln -sf /usr/lib/jvm/java-17-openjdk-amd64/bin/{java,javac,javadoc,jar} /app/data/jdk/bin/ - else - JAVA_PATH=$(which java) - if [ -n "$JAVA_PATH" ]; then - ln -sf $JAVA_PATH /app/data/jdk/bin/java + JAVA_FOUND=0 + for candidate in /usr/lib/jvm/java-21-openjdk-amd64/bin /usr/lib/jvm/java-17-openjdk-amd64/bin; do + if [ -x "$candidate/java" ]; then + ln -sf "$candidate/java" /app/data/jdk/bin/java for tool in javac javadoc jar; do - ln -sf $(which $tool 2>/dev/null) /app/data/jdk/bin/$tool 2>/dev/null || true + [ -x "$candidate/$tool" ] && ln -sf "$candidate/$tool" /app/data/jdk/bin/$tool + done + JAVA_FOUND=1 + break + fi + done + + if [ $JAVA_FOUND -eq 0 ]; then + JAVA_PATH=$(command -v java || true) + if [ -n "$JAVA_PATH" ]; then + ln -sf "$JAVA_PATH" /app/data/jdk/bin/java + for tool in javac javadoc jar; do + TOOL_PATH=$(command -v $tool 2>/dev/null || true) + [ -n "$TOOL_PATH" ] && ln -sf "$TOOL_PATH" /app/data/jdk/bin/$tool done else echo "ERROR: No Java found on system. Elasticsearch requires Java to run." @@ -78,15 +91,13 @@ setup_java() { fi fi - # Verify Java is available - if [ -L /app/data/jdk/bin/java ]; then - echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)" - else + if [ ! -x /app/data/jdk/bin/java ]; then echo "ERROR: Failed to link Java executable" + ls -l /app/data/jdk/bin || true exit 1 fi - # Ensure Java symlinks have correct ownership + echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)" chown -R elasticsearch:elasticsearch /app/data/jdk } @@ -204,12 +215,14 @@ configure_elasticsearch() { chmod 600 $ES_PATH_CONF/{users,users_roles} fi - # Ensure basic settings in elasticsearch.yml - for setting in "xpack.security.http.ssl.enabled: false" "network.host: 0.0.0.0" "discovery.type: single-node"; do - if ! grep -q "$setting" $ES_PATH_CONF/elasticsearch.yml; then - echo "$setting" >> $ES_PATH_CONF/elasticsearch.yml - fi - done + ensure_directory_structure + clean_legacy_settings + ensure_setting "xpack.security.http.ssl.enabled" "false" + ensure_setting "network.host" "0.0.0.0" + ensure_setting "discovery.type" "single-node" + ensure_setting "path.data" "/app/data/elasticsearch/data" + ensure_setting "path.logs" "/app/data/logs" + ensure_setting "path.plugins" "/app/data/elasticsearch/plugins" # Final permission check echo "Final permission check on all data directories..." @@ -360,6 +373,60 @@ setup_keystore() { return 0 } +# Ensure writable dirs exist +ensure_directory_structure() { + mkdir -p /app/data/elasticsearch/{data,plugins} + mkdir -p /app/data/logs + chown -R elasticsearch:elasticsearch /app/data/elasticsearch +} + +clean_legacy_settings() { + sed -i '/^path\.home:/d' "$ES_PATH_CONF/elasticsearch.yml" +} + +ensure_setting() { + local key="$1" + local value="$2" + if grep -q "^$key:" "$ES_PATH_CONF/elasticsearch.yml"; then + sed -i "s|^$key:.*|$key: $value|" "$ES_PATH_CONF/elasticsearch.yml" + else + echo "$key: $value" >> "$ES_PATH_CONF/elasticsearch.yml" + fi +} + +# Install optional Elasticsearch plugins (analysis-icu required for multi-language indexing) +install_plugins() { + local plugin_list="${ES_PLUGINS_INSTALL:-analysis-icu}" + local plugin_dir="/app/data/elasticsearch/plugins" + + if [ -z "$plugin_list" ]; then + echo "No Elasticsearch plugins requested for installation." + return 0 + fi + + # Normalize separators (commas/semicolons/newlines) to whitespace + plugin_list=$(echo "$plugin_list" | tr ',;' ' ') + + for plugin in $plugin_list; do + plugin=$(echo "$plugin" | xargs) + [ -z "$plugin" ] && continue + + if [ -d "$plugin_dir/$plugin" ]; then + echo "Plugin '$plugin' already installed. Skipping." + continue + fi + + echo "Installing Elasticsearch plugin '$plugin'..." + if ! su -c "ES_PATH_CONF=$ES_PATH_CONF ES_JAVA_HOME=/app/data/jdk ES_TMPDIR=/tmp $ES_HOME/bin/elasticsearch-plugin install --batch $plugin" elasticsearch; then + echo "ERROR: Failed to install plugin '$plugin'." + return 1 + fi + done + + echo "✅ Requested Elasticsearch plugins installed." + return 0 +} + # Configure JVM heap size configure_heap() { # Calculate optimal heap size (50% of available memory) @@ -461,7 +528,8 @@ chmod 600 /app/data/secrets/elastic_password set_system_limits configure_heap +install_plugins start_elasticsearch # Keep container running -tail -f /app/data/logs/*.log 2>/dev/null || sleep infinity \ No newline at end of file +tail -f /app/data/logs/*.log 2>/dev/null || sleep infinity