Fix bootstrap templates on read-only volumes

This commit is contained in:
Bertrand Gouny
2026-10-19 10:11:50 +02:00
parent fe5ddbc898
commit bd8218b8a5
7 changed files with 92 additions and 4 deletions
+5
View File
@@ -16,6 +16,11 @@ Project-specific changes only. See upstream [OpenLDAP release notes](https://www
- Test default behavior, automatic role creation and preservation of a group after
deletion of its last real member.
### Fixed
- Render bootstrap LDIF templates in a temporary writable copy, allowing read-only
sources and Kubernetes projected-volume symlinks without modifying source files.
## 2.6.15-beta
### Changed
+2
View File
@@ -21,6 +21,8 @@ own native runner, then publishes them together from their OCI archives
After the shared version check, `tests/` runs integration scenarios on each built image:
- default bootstrap;
- read-only bootstrap templates with projected-volume symlinks, custom overrides
and temporary-copy cleanup on success and failure;
- alternative schemas (`nis`, `virtualMail`);
- auditlog: automatic module loading, writes, custom paths, rotation and restarts;
- last successful binds: disabled by default, precision throttling, failed binds
+9
View File
@@ -15,6 +15,15 @@ const replicationPassword = `Repl passw0rd 'single' \ $HOME`
const defaultSchemas = "core.ldif cosine.ldif inetorgperson.ldif rfc2307bis.ldif"
//go:embed testdata/templates.sh
var templatesCheck string
// ReadOnlyTemplates checks projected-volume sources and temporary-copy cleanup.
func ReadOnlyTemplates(ctx context.Context, client *dagger.Client, image *dagger.Container) error {
_, err := expect(ctx, image, true, nil, "container", "run", "--", "bash", "-c", templatesCheck)
return err
}
// allSchemas can be loaded together. Excluded:
// - nis.ldif and virtualMail.ldif conflict with rfc2307bis.ldif and postfix-book.ldif,
// they are tested by AlternativeSchemas;
+1
View File
@@ -19,6 +19,7 @@ type scenario struct {
var scenarios = []scenario{
{"default bootstrap", Default},
{"read-only bootstrap templates", ReadOnlyTemplates},
{"alternative schemas", AlternativeSchemas},
{"audit logging", AuditLog},
{"last successful binds", LastBind},
+60
View File
@@ -0,0 +1,60 @@
#!/bin/bash
set -euo pipefail
# if container log level is trace:
# print commands and their arguments as they are executed
container log level eq trace && set -x
# shellcheck disable=SC1090
source "${OPENLDAP_TEST_HELPER:-/container/services/openldap-bootstrap/assets/scripts/helpers/common.sh}"
test_dir=$(mktemp -d)
trap 'chmod -R u+rwX "${test_dir:?}"; rm -rf "${test_dir:?}"' EXIT
mkdir -p "${test_dir}/source/..revision" "${test_dir}/output" "${test_dir}/custom"
printf 'dn: %s\n' "\${OPENLDAP_TEST_DN}" > "${test_dir}/source/..revision/10-entry.ldif.template"
printf 'description: original\n' > "${test_dir}/source/..revision/20-plain.ldif"
ln -s ..revision "${test_dir}/source/..data"
ln -s ..data/10-entry.ldif.template "${test_dir}/source/10-entry.ldif.template"
ln -s ..data/20-plain.ldif "${test_dir}/source/20-plain.ldif"
chmod -R a-w "${test_dir}/source"
export OPENLDAP_TEST_DN='dc=example,dc=org'
prepare_ldif_files "${test_dir}/source" "${test_dir}/output"
test "$(cat "${test_dir}/output/10-entry.ldif")" = "dn: ${OPENLDAP_TEST_DN}"
test "$(cat "${test_dir}/output/20-plain.ldif")" = 'description: original'
test ! -e "${test_dir}/source/10-entry.ldif"
test "$(cat "${test_dir}/source/10-entry.ldif.template")" = "dn: \${OPENLDAP_TEST_DN}"
test -L "${test_dir}/source/10-entry.ldif.template"
test -z "$(find "${test_dir}/output" -name '.templates.*')"
printf 'description: custom\n' > "${test_dir}/custom/20-plain.ldif"
prepare_ldif_files "${test_dir}/custom" "${test_dir}/output"
test "$(cat "${test_dir}/output/20-plain.ldif")" = 'description: custom'
printf 'dn: %s\ndescription: custom template\n' "\${OPENLDAP_TEST_DN}" > "${test_dir}/custom/10-entry.ldif.template"
prepare_ldif_files "${test_dir}/custom" "${test_dir}/output"
test "$(cat "${test_dir}/output/10-entry.ldif")" = "$(printf 'dn: %s\ndescription: custom template' "${OPENLDAP_TEST_DN}")"
test ! -e "${test_dir}/custom/10-entry.ldif"
# On failure, the bootstrap's global EXIT trap removes the working directory.
mkdir "${test_dir}/failure"
(
# Invoked by the child Bash process through the exported function.
# shellcheck disable=SC2329
container() {
if [ "${1}" = envsubst ]; then
return 1
fi
return 0
}
export -f container
if bash -c 'set -euo pipefail; cleanup_dir=${3}; trap '\''rm -rf "${cleanup_dir:?}"'\'' EXIT; source "${1}"; prepare_ldif_files "${2}" "${3}"' -- \
"${OPENLDAP_TEST_HELPER:-/container/services/openldap-bootstrap/assets/scripts/helpers/common.sh}" \
"${test_dir}/source" "${test_dir}/failure"; then
exit 1
fi
)
test ! -e "${test_dir}/failure"
test -z "$(find "${test_dir}/output" -name '.templates.*')"
@@ -35,7 +35,7 @@ Utility functions shared across the bootstrap scripts.
|----------|-------------|
| `check_args` | Validates that a script was called with the expected operation (`env`/`ldif`) and argument count |
| `prepare_env_file` | Appends exported variable assignments (safely shell-quoted) to an env file |
| `prepare_ldif_files` | Renders `.ldif.template` files via `container envsubst templates` and copies all resulting `.ldif` files (sorted) to the output directory |
| `prepare_ldif_files` | Copies sources to a temporary writable directory (dereferencing symlinks), renders `.ldif.template` files via `container envsubst templates`, then copies all resulting `.ldif` files (sorted) to the output directory. Sources can be mounted read-only. The function removes its working copy on success; the bootstrap's global EXIT trap cleans up on failure. |
| `get_dn_attr` | Extracts the value of a named attribute from a DN string (e.g. `cn` from `cn=admin,dc=example,dc=com`) |
### `helpers/password.sh`
@@ -33,6 +33,7 @@ prepare_ldif_files() {
local source_dir="$1"
local output_dir="$2"
local work_dir
if [ ! -d "${source_dir}" ]; then
container log debug "Directory not found: ${source_dir}"
@@ -41,13 +42,23 @@ prepare_ldif_files() {
container log info "Load ${source_dir} LDIFs ..."
container log debug "Converting templates to LDIFs in ${source_dir} ..."
container envsubst templates "${source_dir}" 2>&1 | container log debug
work_dir=$(mktemp -d "${output_dir}/.templates.XXXXXX")
# output_dir is inside BOOTSTRAP_TMP_DIR, whose EXIT trap also cleans up
# this working copy if any command below fails.
# Dereference projected-volume symlinks and make read-only sources writable
# only in the working copy.
cp -RL "${source_dir}/." "${work_dir}"
chmod -R u+rwX "${work_dir}"
container log debug "Converting templates to LDIFs in ${work_dir} ..."
container envsubst templates "${work_dir}" 2>&1 | container log debug
while IFS= read -r file; do
cp -frav "${file}" "${output_dir}" 2>&1 | container log debug
done < <(find "${source_dir}" -type f -name "*.ldif" | sort -V)
done < <(find "${work_dir}" -type f -name "*.ldif" | sort -V)
rm -rf "${work_dir:?}"
}
get_dn_attr() {