mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
4 days ago 92cfe956e41a0beee435305c090ffc9daad72445
Report the OpenDJ container healthy only once its bootstrap has succeeded (#898)
5 files modified
112 ■■■■■ changed files
opendj-packages/opendj-docker/Dockerfile 11 ●●●●● patch | view | raw | blame | history
opendj-packages/opendj-docker/Dockerfile-alpine 11 ●●●●● patch | view | raw | blame | history
opendj-packages/opendj-docker/README.md 25 ●●●●● patch | view | raw | blame | history
opendj-packages/opendj-docker/bootstrap/setup.sh 29 ●●●● patch | view | raw | blame | history
opendj-packages/opendj-docker/run.sh 36 ●●●●● patch | view | raw | blame | history
opendj-packages/opendj-docker/Dockerfile
@@ -33,6 +33,8 @@
ENV BACKEND_TYPE="je"
ENV BACKEND_DB_DIRECTORY="db"
#ENV SETUP_ARGS
# written by run.sh once the bootstrap has succeeded, read by the health check below
ENV BOOTSTRAP_COMPLETE="/opt/opendj/.bootstrap-complete"
ARG OPENDJ_DIST_FILENAME=opendj.zip
@@ -66,6 +68,13 @@
USER $OPENDJ_USER
HEALTHCHECK --interval=30s --timeout=30s --start-period=1s --retries=3 CMD opendj/bin/ldapsearch --hostname localhost --port $LDAPS_PORT --bindDN "$ROOT_USER_DN" --bindPassword "${ROOT_PASSWORD:-password}" --useSsl --trustAll --baseDN "" --searchScope base "(objectClass=*)" 1.1 || exit 1
# "healthy" has to mean the instance is ready to serve, not just that it answers: setup
# starts the server in the middle of the bootstrap, before the backend of BASE_DN is
# created and its entries imported, so probing the root DSE alone reports ready while a
# search of BASE_DN still fails with "No Such Entry". Testing the marker first also keeps
# the probe from launching a JVM every interval until the bootstrap is through. The start
# period is what a bootstrap importing SAMPLE_DATA into a small container can take; a
# probe that succeeds ends it early, and a bootstrap that failed never writes the marker.
HEALTHCHECK --interval=30s --timeout=30s --start-period=5m --retries=3 CMD test -f "$BOOTSTRAP_COMPLETE" && opendj/bin/ldapsearch --hostname localhost --port $LDAPS_PORT --bindDN "$ROOT_USER_DN" --bindPassword "${ROOT_PASSWORD:-password}" --useSsl --trustAll --baseDN "" --searchScope base "(objectClass=*)" 1.1 || exit 1
ENTRYPOINT ["/opt/opendj/run.sh"]
opendj-packages/opendj-docker/Dockerfile-alpine
@@ -33,6 +33,8 @@
ENV BACKEND_TYPE="je"
ENV BACKEND_DB_DIRECTORY="db"
#ENV SETUP_ARGS
# written by run.sh once the bootstrap has succeeded, read by the health check below
ENV BOOTSTRAP_COMPLETE="/opt/opendj/.bootstrap-complete"
ARG OPENDJ_DIST_FILENAME=opendj.zip
@@ -70,6 +72,13 @@
USER $OPENDJ_USER
HEALTHCHECK --interval=30s --timeout=30s --start-period=1s --retries=3 CMD opendj/bin/ldapsearch --hostname localhost --port $LDAPS_PORT --bindDN "$ROOT_USER_DN" --bindPassword "${ROOT_PASSWORD:-password}" --useSsl --trustAll --baseDN "" --searchScope base "(objectClass=*)" 1.1 || exit 1
# "healthy" has to mean the instance is ready to serve, not just that it answers: setup
# starts the server in the middle of the bootstrap, before the backend of BASE_DN is
# created and its entries imported, so probing the root DSE alone reports ready while a
# search of BASE_DN still fails with "No Such Entry". Testing the marker first also keeps
# the probe from launching a JVM every interval until the bootstrap is through. The start
# period is what a bootstrap importing SAMPLE_DATA into a small container can take; a
# probe that succeeds ends it early, and a bootstrap that failed never writes the marker.
HEALTHCHECK --interval=30s --timeout=30s --start-period=5m --retries=3 CMD test -f "$BOOTSTRAP_COMPLETE" && opendj/bin/ldapsearch --hostname localhost --port $LDAPS_PORT --bindDN "$ROOT_USER_DN" --bindPassword "${ROOT_PASSWORD:-password}" --useSsl --trustAll --baseDN "" --searchScope base "(objectClass=*)" 1.1 || exit 1
ENTRYPOINT ["/opt/opendj/run.sh"]
opendj-packages/opendj-docker/README.md
@@ -12,11 +12,36 @@
docker run -d -p 1389:1389 -p 1636:1636 -p 4444:4444 --name opendj openidentityplatform/opendj
```
## Health check
The image reports itself `healthy` once the server answers on `LDAPS_PORT` *and* the whole
bootstrap has succeeded - the instance, the `userRoot` backend over `BASE_DN`, whatever
`ADD_BASE_ENTRY` and `SAMPLE_DATA` asked to be imported into it, and the replication asked
for by `MASTER_SERVER`. Waiting for that status is therefore enough before the first search
of what the bootstrap was told to create:
```bash
docker run -d --name opendj -e ADD_BASE_ENTRY=--addBaseEntry openidentityplatform/opendj
timeout 5m bash -c 'until [ "$(docker inspect -f "{{.State.Health.Status}}" opendj)" = healthy ]; do sleep 5; done'
```
In Compose the same is `depends_on: { opendj: { condition: service_healthy } }`. Note that
without `ADD_BASE_ENTRY` nothing creates the base entry, so `BASE_DN` is an empty suffix on
a healthy container - the health check itself searches the root DSE, which every instance
serves whatever it was set up to hold.
A bootstrap that imports `SAMPLE_DATA` can take minutes on a small container, which is what
the start period allows for. A bootstrap that fails - or an upgrade that fails when starting
over an instance that is already there - never reports healthy: what failed is in `docker
logs`, and where the server is up at all the container is left running to be looked at,
turning `unhealthy` once the start period is over.
## Environment Variables
| Variable                | Default Value                   | Description                                                                                                                                                                                                                                             |
|-------------------------|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ADD_BASE_ENTRY          |                                 | if set --addBaseEntry , creates base DN entry                                                                                                                                                                                                           |
| SAMPLE_DATA             | -                               | with ADD_BASE_ENTRY set, imports that many generated users under BASE_DN instead of the base entry alone                                                                                                                                                 |
| PORT                    | 1389                            | LDAP Listener Port                                                                                                                                                                                                                                      |
| LDAPS_PORT              | 1636                            | LDAPS Listener Port                                                                                                                                                                                                                                     |
| BASE_DN                 | dc=example,dc=com               | OpenDJ Base DN                                                                                                                                                                                                                                          |
opendj-packages/opendj-docker/bootstrap/setup.sh
@@ -1,5 +1,24 @@
#!/usr/bin/env bash
# The contents of this file are subject to the terms of the Common Development and
# Distribution License (the License). You may not use this file except in compliance with the
# License.
#
# You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
# specific language governing permission and limitations under the License.
#
# When distributing Covered Software, include this CDDL Header Notice in each file and include
# the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
# Header, with the fields enclosed by brackets [] replaced by your own identifying
# information: "Portions copyright [year] [name of copyright owner]".
#
# Portions copyright 2026 3A Systems, LLC.
# Default setup script
#
# What the instance is made of - the server itself, the backend, the entries the backend was
# asked to hold - is left to fail this script, which is what run.sh reads to decide whether
# the container may report itself healthy. The optional schema and data LDIFs below keep the
# tolerance they were written with.
echo "Setting up default OpenDJ instance"
@@ -30,7 +49,7 @@
  --acceptLicense \
  --no-prompt \
  --noPropertiesFile \
  $SETUP_ARGS
  $SETUP_ARGS || exit 1
BACKEND_TYPE=${BACKEND_TYPE:-je}
BACKEND_DB_DIRECTORY=${BACKEND_DB_DIRECTORY:-db}
@@ -38,21 +57,21 @@
/opt/opendj/bin/dsconfig create-backend -h localhost -p $ADMIN_PORT --bindDN "$ROOT_USER_DN" --bindPassword "$ROOT_PASSWORD" \
  --backend-name=userRoot --type $BACKEND_TYPE --set base-dn:$BASE_DN --set "db-directory:$BACKEND_DB_DIRECTORY" \
  --set enabled:true --no-prompt --trustAll
  --set enabled:true --no-prompt --trustAll || exit 1
if [ "$ADD_BASE_ENTRY" = "--addBaseEntry"  ]; then
  BASE_TEMPLATE=$(mktemp)
  if [ ! -z ${SAMPLE_DATA} ]; then
    echo "generating sample data..."
    /opt/opendj/bin/makeldif -o $BASE_TEMPLATE -c suffix="$BASE_DN" -c numusers=$SAMPLE_DATA /opt/opendj/template/config/MakeLDIF/example.template
    /opt/opendj/bin/makeldif -o $BASE_TEMPLATE -c suffix="$BASE_DN" -c numusers=$SAMPLE_DATA /opt/opendj/template/config/MakeLDIF/example.template || exit 1
    /opt/opendj/bin/import-ldif --ldifFile $BASE_TEMPLATE \
        --backendID=userRoot --bindDN "$ROOT_USER_DN" --bindPassword "$ROOT_PASSWORD"
        --backendID=userRoot --bindDN "$ROOT_USER_DN" --bindPassword "$ROOT_PASSWORD" || exit 1
  else
    echo "creating base entry..."
    BASE_TEMPLATE=$(mktemp)
    echo "branch: $BASE_DN" > $BASE_TEMPLATE
    /opt/opendj/bin/import-ldif --templateFile $BASE_TEMPLATE \
        --backendID=userRoot --bindDN "$ROOT_USER_DN" --bindPassword "$ROOT_PASSWORD"
        --backendID=userRoot --bindDN "$ROOT_USER_DN" --bindPassword "$ROOT_PASSWORD" || exit 1
  fi
  rm $BASE_TEMPLATE
fi
opendj-packages/opendj-docker/run.sh
@@ -23,6 +23,17 @@
cd /opt/opendj
# The health check probes the server only once this marker is there, so that "healthy"
# means the instance is bootstrapped rather than merely listening: setup starts the
# server in the middle of the bootstrap, before the backend holding BASE_DN has been
# created. Nothing below writes it unless the step it stands for reported success, so a
# bootstrap that failed leaves the container running to be looked at, but never healthy.
# It is kept outside ./data because it records what this container has done, not what
# the volume holds - and a restart of a container replays this script over the writable
# layer the previous run left behind, so it is cleared before anything else.
BOOTSTRAP_COMPLETE=${BOOTSTRAP_COMPLETE:-/opt/opendj/.bootstrap-complete}
rm -f "$BOOTSTRAP_COMPLETE"
#if default data folder exists do not change it
if [ ! -d ./db ]; then
  echo "/opt/opendj/data" >/opt/opendj/instance.loc && \
@@ -31,7 +42,13 @@
# Instance dir does exist? We start opendj without detach
if [ -d ./data/config ]; then
  sh ./upgrade -n
  # nothing is bootstrapped here, the instance is already there - but a half-migrated one
  # is not ready to serve either, so the marker follows the upgrade
  if sh ./upgrade -n; then
    touch "$BOOTSTRAP_COMPLETE"
  else
    echo "Upgrade failed, this container will not report itself healthy"
  fi
  exec ./bin/start-ds --nodetach
  exit
fi
@@ -46,11 +63,18 @@
BOOTSTRAP=${BOOTSTRAP:-/opt/opendj/bootstrap/setup.sh}
echo "Running $BOOTSTRAP"
sh "${BOOTSTRAP}"
BOOTSTRAPPED=true
if ! sh "${BOOTSTRAP}"; then
  BOOTSTRAPPED=false
  echo "$BOOTSTRAP failed, this container will not report itself healthy"
fi
# Check if OPENDJ_REPLICATION_TYPE var is set. If it is - replicate to that server
if [ -n "${MASTER_SERVER}" ] && [ -n "${OPENDJ_REPLICATION_TYPE}" ]; then
  /opt/opendj/bootstrap/replicate.sh
  if ! /opt/opendj/bootstrap/replicate.sh; then
    BOOTSTRAPPED=false
    echo "Replication setup failed, this container will not report itself healthy"
  fi
fi
# Check if keystores are mounted as a volume, and if so
@@ -63,6 +87,12 @@
  cp -f ${SECRET_VOLUME}/key* ${SECRET_VOLUME}/trust* ./data/config 2>/dev/null
fi
# Everything the instance was asked to be set up with - its backend, its base entry, its
# replication - is in place from here on, so the health check may start probing the server
if [ "$BOOTSTRAPPED" = true ]; then
  touch "$BOOTSTRAP_COMPLETE"
fi
# Opendj is probably already started in detach mode at the install
if (bin/status -n | grep Started); then
  echo "OpenDJ is started"