This is an automated email from the ASF dual-hosted git repository. danwatford pushed a commit to branch experimental-docker in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
commit 0ef7f9c52a473ab561f7ccad57fa0a340351d60b Author: Daniel Watford <dan...@watfordconsulting.com> AuthorDate: Wed Feb 22 16:54:16 2023 +0000 Implemented: Build OFBiz as a docker container image (OFBIZ-12757) Added a Dockerfile to define a build based on currently checked-out sources. Changes include an initialisation script for launching an OFBiz container which configures OFBiz settings for the current environment. --- .dockerignore | 7 ++ DOCKER.md | 157 +++++++++++++++++++++++ Dockerfile | 79 ++++++++++++ build.gradle | 8 ++ docker/docker-entrypoint.sh | 260 +++++++++++++++++++++++++++++++++++++++ docker/send_ofbiz_stop_signal.sh | 39 ++++++ 6 files changed, 550 insertions(+) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..86fb0090f5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +Dockerfile +.gradle/ +/bin/ +/build/ +/runtime/ +/themes/common-theme/webapp/common/js/node_modules/ +/themes/common-theme/webapp/common-theme/js/node_modules/ diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000000..2af492dac8 --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,157 @@ +# Building and running OFBiz using Docker + +OFBiz includes a Dockerfile which can be used to build a container image for running OFBiz. +The container image is built based on the sources already available in the build +content, i.e. it only uses sources that you have already downloaded. + +If you want to include any plugins in your container image, you must download those +plugins before running the build. (See the plugin documentation in README.adoc). + +## Quickstart +Follow these instructions to qet started building and running OFBiz using Docker. + +### Build the OFBiz container image + +From the sources directory (i.e. the directory containing Docker.md), run +```shell script +DOCKER_BUILDKIT=1 docker build --tag ofbiz-docker . +``` + +### Run the OFBiz container + +Run the following command: +```shell script +docker run -it -e OFBIZ_DATA_LOAD=demo --name ofbiz-docker -p 8443:8443 ofbiz-docker +``` + +This will start an instance of the ofbiz-docker container, publish port 8443 to localhost, +load the OFBiz demo data, and then run the OFBiz server. + +Once start up completes, you can access OFBIZ at http://localhost:8443/partymgr + +## Other container building options + +The OFBiz `Dockerfile` defines a mult-stage build. The default container image produced by the build is named `runtime` +and consists of an unintialized OFBiz installation. + +During the first run of a container based on this image, seed or demo data can be loaded, and an admin user created. + +The runtime container is the default target of a docker build and can be created with commands similar to: +```shell script +DOCKER_BUILDKIT=1 docker build --tag ofbiz-docker . +``` +OR +```shell script +DOCKER_BUILDKIT=1 docker build --target runtime --tag ofbiz-docker . +``` + +The Dockerfile also defines another stage, `demo`, which produces a container image with demonstration data already +loaded. This container image may be useful in cases where containers are frequency run and destroyed and the time +taken to load demo data is becoming noticeable. + +To build a container image pre-loaded with demo data, run: +```shell script +DOCKER_BUILDKIT=1 docker build --target runtime --tag ofbiz-docker . +``` + +## Container runtime options + +The container's behaviour at runtime is controlled via environment variables, 'hook' scripts and XML entity import +files. These items will only be applied to a container during its first run. Flags stored in +`/ofbiz/runtime/container_state` will prevent the repeated application of these items during subsequent starts of the +container. + +Use of environment variables, hook scripts and XML entity import files are manged by the docker-entrypoint.sh script. + +### Environment variables +Environment variables are used in `docker-entrypoint.sh` to control configuration options for the OFBiz container. + +|Environment variable | Default value | Description | +|---|---|---| +|OFBIZ_SKIP_INIT | *empty* | Any non-empty value will cause this script to skip any initialisation steps. | +|OFBIZ_ADMIN_USER | admin | Sets the username of the OFBIZ admin user. | +|OFBIZ_ADMIN_PASSWORD | ofbiz | Sets the password of the OFBIZ admin user. | +|OFBIZ_DATA_LOAD | seed | Determine what type of data loading is required. *none*: No data loading is perfomed. *seed*: Seed data is loaded. *demo*: Demo data is loaded. | +|OFBIZ_HOST | localhost | Specify the hostname used to access OFBiz. | +|OFBIZ_CONTENT_URL_PREFIX | https://${OFBIZ_HOST} | Used to set the content.url.prefix.secure and content.url.prefix.standard properties in `framework/webapp/config/url.properties`. | +|OFBIZ_ENABLE_AJP_PORT | *empty* | Enable the AJP (Apache JServe Protocol) port to allow communication with OFBiz via a reverse proxy. Enabled when this environment variable contains a non-empty value. | + +### Hooks +At various steps of initialisation, the `docker-entrypoint.sh` script will check for +'hook' scripts in various directories. + +Users of the container can mount files into these directories and influence the OFBiz startup process. + +Only script files with filename extension `.sh` will be processed. If the file is executable, it will +be executed, otherwise it will be sourced. + +|Directory | Step | +|---|---| +| `/docker-entrypoint-before-config-applied.d` | Scripts processed before configuration, such as modifications to property files, are applied. | +| `/docker-entrypoint-after-config-applied.d` | Scripts processed after configuration modifications have been applied. | +| `/docker-entrypoint-before-data-load.d` | Scripts processed before data loading is executed. Could be used to apply modifications to data files.| +| `/docker-entrypoint-after-data-load.d` | Scripts processed after data loading is executed. | + +### Data files +During the data loading step - but after either seed or demo data has been loaded - directory +`/docker-entrypoint-additional-data.d` will be checked to see if any files are present. + +If files are present then the load-data functionality in OFBiz will be executed, specifying the +`/docker-entrypoint-additional-data.d` directory as a data source. Any `.xml` files in this +directory will be treated as a data source and will be imported by the entity engine. + +This functionality can be used to pre-load OFBiz with user-specific data, such as +a chart of accounts. + +## Examples of running the OFBiz container + +``` +docker run -it -p 8443:8443 ofbiz-docker +``` +Launch the OFBiz container, load the seed data, create the administrator user with +name `admin` and password `ofbiz`, listen on port 8443 for connections to `localhost`. + +Users can access OFBiz at https://localhost:8443/partymgr + +The docker container will remain attached the terminal. Interrupting the container, +i.e. pressing Ctrl-C, will trigger a graceful shutdown of the container. + +``` +docker run -it -e OFBIZ_DATA_LOAD=demo -p 8443:8443 ofbiz-docker +``` +Launch the OFBiz container, load the demo data, listen on port 8443 for connections to `localhost`. + +The demo data includes the administrator user with name `admin` and password `ofbiz`. + +``` +docker run -it -e OFBIZ_DATA_LOAD=seed -e OFBIZ_ADMIN_USER=localadmin -e OFBIZ_ADMIN_PASSWORD=TTTTT -p 8443:8443 ofbiz-docker +``` +Launch the OFBiz container, load the seed data, create the administrator user with +name `localadmin` and password `TTTTT`, listen on port 8443 for connections to `localhost`. + +``` +docker run -it -v 'C:\ofbiz-framework\add-data':/docker-entrypoint-additional-data.d -p 8443:8443 ofbiz-docker +``` +Example of running on Windows. + +Launches the container with default seed data and administrator user. + +After data is loaded, any `.xml` files in directory `C:\ofbiz-framework\add-data` are imported by +the OFBiz entity engine. + +``` +docker run -it -p 8443:8443 ofbiz-docker +``` +Launch the OFBiz container, load the seed data, create the administrator user with +name `admin` and password `ofbiz`, listen on port 8443 for connections to `localhost`. + +Users can access OFBiz at https://localhost:8443/partymgr + +The docker container will remain attached the terminal. Interrupting the container, +i.e. pressing Ctrl-C, will trigger a graceful shutdown of the container. + + +``` +docker run -it -e OFBIZ_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" -p 8443:8443 -p 5005:5005 ofbiz-docker +``` +Creates a debuggable instance of OFBiz, listening on port 5005. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..9dcd87e0d8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,79 @@ +# syntax=docker/dockerfile:1 +FROM eclipse-temurin:17 as builder + +# Git is used for various OFBiz build tasks. +RUN apt-get update \ + && apt-get install -y git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /builder + +# Add and run the gradle wrapper to trigger a download if needed. +COPY gradle/init-gradle-wrapper.sh gradle/ +COPY gradlew . +RUN ["sed", "-i", "s/shasum/sha1sum/g", "gradle/init-gradle-wrapper.sh"] +RUN ["gradle/init-gradle-wrapper.sh"] + +# Run gradlew to trigger downloading of the gradle distribution (if needed) +RUN --mount=type=cache,id=gradle-cache,sharing=locked,target=/root/.gradle \ + ["./gradlew", "--console", "plain"] + +# Copy all OFBiz sources. +COPY . . + +# Build OFBiz while mounting a gradle cache +RUN --mount=type=cache,id=gradle-cache,sharing=locked,target=/root/.gradle \ + --mount=type=tmpfs,target=runtime/tmp \ + ["./gradlew", "--console", "plain", "distTar"] + +################################################################################### + +FROM eclipse-temurin:17 as runtimebase + +RUN ["useradd", "ofbiz"] + +# Configure volumes where hooks into the startup process can be placed. +RUN ["mkdir", "/docker-entrypoint-before-config-applied.d", "/docker-entrypoint-after-config-applied.d", \ + "/docker-entrypoint-before-data-load.d", "/docker-entrypoint-after-data-load.d", \ + "/docker-entrypoint-additional-data.d"] +RUN ["sh", "-c", "/usr/bin/chown -R ofbiz:ofbiz /docker-entrypoint-*.d" ] + +USER ofbiz +WORKDIR /ofbiz + +# Extract the OFBiz tar distribution created by the builder stage. +RUN --mount=type=bind,from=builder,source=/builder/build/distributions/ofbiz.tar,target=/mnt/ofbiz.tar \ + ["tar", "--extract", "--strip-components=1", "--file=/mnt/ofbiz.tar"] + +COPY docker/docker-entrypoint.sh . +COPY docker/send_ofbiz_stop_signal.sh . + +EXPOSE 8443 +EXPOSE 8009 +EXPOSE 5005 + +ENTRYPOINT ["/ofbiz/docker-entrypoint.sh"] +CMD ["bin/ofbiz"] + +################################################################################### +# Load demo data before defining volumes. This results in a container image +# that is ready to go for demo purposes. +FROM runtimebase as demo + +RUN /ofbiz/bin/ofbiz --load-data +RUN mkdir --parents /ofbiz/runtime/container_state +RUN touch /ofbiz/runtime/container_state/data_loaded +RUN touch /ofbiz/runtime/container_state/admin_loaded + +VOLUME ["/docker-entrypoint-before-config-applied.d", "/docker-entrypoint-after-config-applied.d", \ + "/docker-entrypoint-before-data-load.d", "/docker-entrypoint-after-data-load.d", \ + "/docker-entrypoint-additional-data.d"] + + +################################################################################### +# Runtime image with no data loaded. +FROM runtimebase as runtime + +VOLUME ["/docker-entrypoint-before-config-applied.d", "/docker-entrypoint-after-config-applied.d", \ + "/docker-entrypoint-before-data-load.d", "/docker-entrypoint-after-data-load.d", \ + "/docker-entrypoint-additional-data.d"] diff --git a/build.gradle b/build.gradle index 1a66fb4277..82b50374fb 100644 --- a/build.gradle +++ b/build.gradle @@ -125,6 +125,14 @@ node { npmInstallCommand = System.getenv("CI") ? 'ci' : 'install' } +tasks.getByName("distTar") { + dependsOn "npmInstall" +} + +tasks.getByName("distZip") { + dependsOn "npmInstall" +} + java { sourceCompatibility(JavaVersion.VERSION_17) targetCompatibility(JavaVersion.VERSION_17) diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100644 index 0000000000..d6ac5e8368 --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,260 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +############################################################################### + +############################################################################### +# OFBiz initialisation script for use as the entry point in a docker container. +# +# Triggers the loading of data and configuration of various OFBiz properties before +# executing the command given as arguments to the script. +# +# Behaviour controlled by environment variables: +# +# OFBIZ_SKIP_INIT +# Any non-empty value will cause this script to skip any initialisation steps. +# Default: <empty> +# +# OFBIZ_ADMIN_USER +# The username of the OFBIZ admin user. +# Default: admin +# +# OFBIZ_ADMIN_PASSWORD +# The password of the OFBIZ admin user. +# Default: ofbiz +# +# OFBIZ_DATA_LOAD +# Determine what type of data loading is required. +# Default: seed +# Values: +# - none: No data loading is performed. +# - seed: Seed data is loaded. +# - demo: Demo data is loaded. +# +# OFBIZ_HOST +# Specify the hostname used to access OFBiz. +# Used to populate the host-headers-allowed property in framework/security/config/security.properties. +# Default: localhost +# +# OFBIZ_CONTENT_URL_PREFIX +# Used to set the content.url.prefix.secure and content.url.prefix.standard properties in +# framework/webapp/config/url.properties. +# Default: https://${OFBIZ_HOST} +# +# OFBIZ_ENABLE_AJP_PORT +# Enable the AJP (Apache JServe Protocol) port to allow communication with OFBiz via a reverse proxy. +# Enabled when this environment variable contains a non-empty value. +# Default value: <empty> +# +# Hooks are executed at the various stages of the initialisation process by executing scripts in the following +# directories. Scripts must be executable and have the .sh extension: +# +# /docker-entrypoint-before-data-load.d +# Executed before any data loading is about to be performed. Only executed if data loading is required. +# Example usage would be to alter the data to be loaded. +# +# /docker-entrypoint-after-data-load.d +# Executed after any data loading has been performed. Only executed if data loading was required. +# +############################################################################### +set -x +set -e + +trap shutdown_ofbiz SIGTERM SIGINT + +CONTAINER_STATE_DIR="/ofbiz/runtime/container_state" +CONTAINER_DATA_LOADED="$CONTAINER_STATE_DIR/data_loaded" +CONTAINER_ADMIN_LOADED="$CONTAINER_STATE_DIR/admin_loaded" +CONTAINER_CONFIG_APPLIED="$CONTAINER_STATE_DIR/config_applied" + +############################################################################### +# Validate and apply defaults to any environment variables used by this script. +# See script header for environment variable descriptions. +ofbiz_setup_env() { + case "$OFBIZ_DATA_LOAD" in + none | seed | demo) ;; + *) + OFBIZ_DATA_LOAD="none" + ;; + esac + + OFBIZ_ADMIN_USER=${OFBIZ_ADMIN_USER:-admin} + + OFBIZ_ADMIN_PASSWORD=${OFBIZ_ADMIN_PASSWORD:-ofbiz} + + OFBIZ_HOST=${OFBIZ_HOST:-localhost} + + OFBIZ_CONTENT_URL_PREFIX=${OFBIZ_CONTENT_URL_PREFIX:-https://${OFBIZ_HOST}} +} + +############################################################################### +# Create the runtime container state directory used to track which initialisation +# steps have been run for the container. +# This directory should be hosted on a volume that persists for the life of the container. +create_ofbiz_runtime_directories() { + if [ ! -d "$CONTAINER_STATE_DIR" ]; then + mkdir --parents "$CONTAINER_STATE_DIR" + fi +} + +############################################################################### +# Execute the shell scripts at the paths passed to this function. +# Args: +# 1: Name of the hook stage being executed. Used for logging. +# 2+: Variable number of paths to the shell scripts to be executed. +# Only scripts with the .sh extension are executed. +# Scripts will be sourced if they are not executable. +run_init_hooks() { + local hookStage="$1" + shift + local filePath + for filePath; do + case "$filePath" in + *.sh) + if [ -x "$filePath" ]; then + printf '%s: running %s\n' "$hookStage" "$filePath" + "$filePath" + else + printf '%s: sourcing %s\n' "$hookStage" "$filePath" + . "$filePath" + fi + ;; + *) + printf '%s: Not a script. Ignoring %s\n' "$hookStage" "$filePath" + ;; + esac + done +} + +############################################################################### +# If required, load data into OFBiz. +load_data() { + if [ ! -f "$CONTAINER_DATA_LOADED" ]; then + run_init_hooks /docker-entrypoint-before-data-load.d/* + + case "$OFBIZ_DATA_LOAD" in + none) ;; + + seed) + /ofbiz/bin/ofbiz --load-data readers=seed,seed-initial + ;; + + demo) + /ofbiz/bin/ofbiz --load-data + # Demo data includes the admin user so indicate that the user is already loaded. + touch "$CONTAINER_ADMIN_LOADED" + ;; + esac + + # Load any additional data files provided. + if [ -z $(find /docker-entrypoint-additional-data.d/ -prune -empty) ]; then + /ofbiz/bin/ofbiz --load-data dir=/docker-entrypoint-additional-data.d + fi + + touch "$CONTAINER_DATA_LOADED" + + run_init_hooks /docker-entrypoint-after-data-load.d/* + fi +} + +############################################################################### +# Create and load the password hash for the admin user. +load_admin_user() { + if [ ! -f "$CONTAINER_ADMIN_LOADED" ]; then + TMPFILE=$(mktemp) + + # Concatenate a random salt and the admin password. + SALT=$(tr --delete --complement A-Za-z0-9 </dev/urandom | head --bytes=16) + SALT_AND_PASSWORD="${SALT}${OFBIZ_ADMIN_PASSWORD}" + + # Take a SHA-1 hash of the combined salt and password and strip off any additional output form the sha1sum utility. + SHA1SUM_ASCII_HEX=$(printf "$SALT_AND_PASSWORD" | sha1sum | cut --delimiter=' ' --fields=1 --zero-terminated | tr --delete '\000') + + # Convert the ASCII Hex representation of the hash to raw bytes by inserting escape sequences and running + # through the printf command. Encode the result as URL base 64 and remove padding. + SHA1SUM_ESCAPED_STRING=$(printf "$SHA1SUM_ASCII_HEX" | sed -e 's/\(..\)\.\?/\\x\1/g') + SHA1SUM_BASE64=$(printf "$SHA1SUM_ESCAPED_STRING" | basenc --base64url --wrap=0 | tr --delete '=') + + # Concatenate the hash type, salt and hash as the encoded password value. + ENCODED_PASSWORD_HASH="\$SHA\$${SALT}\$${SHA1SUM_BASE64}" + + # Populate the login data template + sed "s/@userLoginId@/$OFBIZ_ADMIN_USER/g; s/currentPassword=\".*\"/currentPassword=\"$ENCODED_PASSWORD_HASH\"/g;" framework/resources/templates/AdminUserLoginData.xml >"$TMPFILE" + + # Load data from the populated template. + /ofbiz/bin/ofbiz --load-data "file=$TMPFILE" + + rm "$TMPFILE" + + touch "$CONTAINER_ADMIN_LOADED" + fi +} + +############################################################################### +# Apply any configuration changes required. +apply_configuration() { + if [ ! -f "$CONTAINER_CONFIG_APPLIED" ]; then + run_init_hooks /docker-entrypoint-before-config-applied.d/* + + if [ -n "$OFBIZ_ENABLE_AJP_PORT" ]; then + # Configure tomcat to listen for AJP connections on all interfaces within the container. + sed --in-place \ + '/<property name="ajp-connector" value="connector">/ a <property name="address" value="0.0.0.0"/>' \ + /ofbiz/framework/catalina/ofbiz-component.xml + fi + + sed --in-place \ + "s/host-headers-allowed=.*/host-headers-allowed=${OFBIZ_HOST}/" framework/security/config/security.properties + + sed --in-place \ + --expression="s#content.url.prefix.secure=.*#content.url.prefix.secure=${OFBIZ_CONTENT_URL_PREFIX}#;" \ + --expression="s#content.url.prefix.standard=.*#content.url.prefix.standard=${OFBIZ_CONTENT_URL_PREFIX}#;" \ + framework/webapp/config/url.properties + + touch "$CONTAINER_CONFIG_APPLIED" + run_init_hooks /docker-entrypoint-after-config-applied.d/* + fi +} + +############################################################################### +# Send a shutdown signal to OFBiz +shutdown_ofbiz() { + /ofbiz/send_ofbiz_stop_signal.sh +} + +_main() { + if [ -z "$OFBIZ_SKIP_INIT" ]; then + ofbiz_setup_env + create_ofbiz_runtime_directories + apply_configuration + load_data + load_admin_user + fi + + # Continue loading OFBiz. + unset OFBIZ_SKIP_INIT + unset OFBIZ_ADMIN_USER + unset OFBIZ_ADMIN_PASSWORD + unset OFBIZ_DATA_LOAD + unset OFBIZ_ENABLE_AJP_PORT + unset OFBIZ_HOST + unset OFBIZ_CONTENT_URL_PREFIX + exec "$@" +} + +_main "$@" + diff --git a/docker/send_ofbiz_stop_signal.sh b/docker/send_ofbiz_stop_signal.sh new file mode 100644 index 0000000000..6a15c120c2 --- /dev/null +++ b/docker/send_ofbiz_stop_signal.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +############################################################################### + +set -e + +# Read a property value. Assumes all properties are single line and do not have spaces around the '=' sign or comments following the valuel. +function getPropertyValue +{ + echo "$1" | sed "/^$2=/!d; s///" +} + +echo "Getting admin port and key..." +START_PROPERTIES_CONTENT=$(cat /ofbiz/framework/start/src/main/resources/org/apache/ofbiz/base/start/start.properties) + +OFBIZ_ADMIN_PORT=$(getPropertyValue "$START_PROPERTIES_CONTENT" "ofbiz.admin.port") +echo Admin port: $OFBIZ_ADMIN_PORT; + +OFBIZ_ADMIN_KEY=$(getPropertyValue "$START_PROPERTIES_CONTENT" "ofbiz.admin.key") +echo Admin key: $OFBIZ_ADMIN_KEY; + +echo "Sending shutdown signal..." +echo "$OFBIZ_ADMIN_KEY:SHUTDOWN" | curl telnet://localhost:$OFBIZ_ADMIN_PORT +echo "Done"