#!/bin/bash
# Run only inside the disposable container created by run_matrix.py.
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
[[ -f /run/sdp-container-test && -d /inputs/sdp ]] || fail 'Missing isolated container marker/source.'
[[ $(cat /proc/1/comm) == systemd ]] || fail 'Container PID 1 must be systemd.'
RELEASE=${1:-r26.1}
INITIALIZE=(--initialize)
case "${2:-}" in "") ;; --resume) INITIALIZE=();; *) fail "Unknown bootstrap option: $2";; esac
[[ "$RELEASE" =~ ^r[0-9]{2}\.[0-9]+$ ]] || fail 'Invalid Perforce release.'
mkdir -p /inputs/binaries /results /run/sshd || exit 1
cp /etc/os-release /results/os-release || exit 1
uname -a > /results/uname.txt
systemctl --version > /results/systemd-version.txt
bash /inputs/sdp/test/containers/download_binaries.sh "$RELEASE" /inputs/binaries /results || fail 'Binary acquisition failed.'
# The source exporter records exact current workspace contents, including unsubmitted edits.
cp /inputs/sdp/.export-manifest.json /results/source-manifest.json || fail 'Export manifest is required.'
cp /inputs/sdp/.export-manifest.json /results/installation-source-manifest.json || fail 'Installation source manifest could not be preserved.'
cp /inputs/baseline-sdp/.export-manifest.json /results/baseline-source-manifest.json || fail 'Baseline manifest is required.'
cp /inputs/baseline-sdp/.baseline-info.json /results/baseline-info.json || fail 'Baseline provenance metadata is required.'
# A repeated bootstrap replaces this fixture's service together with its data.
# Remove only the unit generated below; reject any unrelated existing unit.
if [[ -e /etc/systemd/system/p4d_1.service ]]; then
(( ${#INITIALIZE[@]} == 0 )) || fail 'Existing service requires explicit --resume.'
grep -Fx 'Description=Disposable SDP Helix Core instance 1' /etc/systemd/system/p4d_1.service >/dev/null || fail 'Refusing to replace an unrelated service unit.'
systemctl stop p4d_1.service || fail 'Cannot stop previous disposable unit.'
systemctl disable p4d_1.service || fail 'Cannot disable previous disposable unit.'
rm /etc/systemd/system/p4d_1.service || exit 1
systemctl daemon-reload || exit 1
fi
RESET_RC=0
/inputs/sdp/test/reset.sh --root /srv/sdp-test-integration --instance 1 --port 19999 --os-user perforce \
--source /inputs/sdp --p4 /inputs/binaries/p4 --p4d /inputs/binaries/p4d \
--p4broker /inputs/binaries/p4broker --p4p /inputs/binaries/p4p \
"${INITIALIZE[@]}" --confirm-disposable > /results/provision.log 2>&1 || RESET_RC=$?
if [[ -f /srv/sdp-test-integration/mkdirs.cfg ]]; then
cp /srv/sdp-test-integration/mkdirs.cfg /results/mkdirs.cfg || exit 1
sha256sum /results/mkdirs.cfg > /results/mkdirs.cfg.sha256 || exit 1
printf '/srv/sdp-test-integration/sdp/Server/Unix/setup/mkdirs.sh /srv/sdp-test-integration/mkdirs.cfg\n' > /results/installer-command.txt || exit 1
fi
(( RESET_RC == 0 )) || fail 'SDP provisioning failed; see /results/provision.log.'
[[ -f /results/mkdirs.cfg ]] || fail 'Successful reset did not preserve mkdirs.cfg.'
grep -F 'Installer command: /srv/sdp-test-integration/sdp/Server/Unix/setup/mkdirs.sh /srv/sdp-test-integration/mkdirs.cfg' /results/provision.log >/dev/null || fail 'Missing evidence that mkdirs.sh used the recorded configuration.'
# Hand the already provisioned server over to a real systemd unit.
/p4/1/bin/p4d_1_init stop >> /results/provision.log 2>&1 || fail 'Could not stop standalone server.'
cat > /etc/systemd/system/p4d_1.service <<'UNIT'
[Unit]
Description=Disposable SDP Helix Core instance 1
After=network.target
[Service]
Type=forking
User=perforce
Environment=OS_INIT_MECHANISM=systemd
PIDFile=/p4/1/root/server.pid
ExecStart=/p4/1/bin/p4d_1_init start
ExecStop=/p4/1/bin/p4d_1_init stop
TimeoutStartSec=180
TimeoutStopSec=180
[Install]
WantedBy=multi-user.target
UNIT
[[ $? -eq 0 ]] || exit 1
printf 'perforce ALL=(root) NOPASSWD: /usr/bin/systemctl * p4d_1, /usr/bin/systemctl * p4d_1.service\n' > /etc/sudoers.d/sdp-container-test || exit 1
chmod 440 /etc/sudoers.d/sdp-container-test || exit 1
visudo -cf /etc/sudoers.d/sdp-container-test || fail 'Invalid disposable sudo rule.'
systemctl daemon-reload && systemctl enable --now p4d_1.service || fail 'Systemd server start failed.'
systemctl show p4d_1.service -p ActiveState -p SubState -p MainPID > /results/service-state.txt
[[ $(systemctl show p4d_1.service -p MainPID --value) =~ ^[1-9][0-9]*$ ]] || fail 'Systemd has no server MainPID.'
# The actual source-built interpreter is primary, with a separate supported
# distro interpreter for an additional matrix run. Neither replaces system Python.
bash /inputs/sdp/test/containers/python_runtime.sh install || fail 'Python runtime setup failed.'
bash /inputs/sdp/test/containers/python_runtime.sh select baseline || exit 1
export PATH="/p4/common/python/bin:$PATH"
runuser -u perforce -- bash -c 'source /p4/common/bin/p4_vars 1 || exit 1; printf "%s\n" "$P4JOURNAL"' > /results/journal-path.txt || fail 'Cannot read installed journal configuration.'
[[ $(cat /results/journal-path.txt) == /p4/1/journals/journal && -f /p4/1/journals/journal ]] || fail 'Live journal does not use the required journals directory.'
python3 - <<'PY'
import json,pathlib,subprocess
pid=int(subprocess.check_output(['systemctl','show','p4d_1.service','-p','MainPID','--value'],text=True))
argv=pathlib.Path('/proc/%d/cmdline'%pid).read_bytes().decode().strip('\0').split('\0')
assert '-J' in argv and argv[argv.index('-J')+1]=='/p4/1/journals/journal',argv
pathlib.Path('/results/p4d-process-argv.json').write_text(json.dumps({'pid':pid,'argv':argv,'journal':'/p4/1/journals/journal'},indent=2)+'\n')
PY
[[ $? == 0 ]] || fail 'Running p4d journal path differs from configuration.'
bash /inputs/sdp/test/containers/sudoers_preview.sh > /results/sudoers-preview-console.log 2>&1 || fail 'Sudoers preview validation failed.'
printf 'Provisioning complete; systemd owns the disposable instance.\n'
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #1 | 34004 | Russell C. Jackson (Rusty) |
Improve SDP backup/recovery, Maintenance, logging and cloud operation. Add owned checkpoint locks, recovery-aware retention, preserved database switches, bounded cleanup and coordinated AWS/Azure snapshots. Fix service-user password initialization, edge staging, standby promotion state parsing, proxy cache setup, typed depot verification and historical-server configuration/error handling. Remove unused OTP programs; keep Windows removed, server.locks cleanup and the journal-count condition unchanged. Add no global shell strict modes. Update operational documentation and add reproducible mkdirs.sh/mkdirs.cfg container assurance. Validate Ubuntu 26.04 and UBI 8.10/9.8/10.2 across both Python runtimes: 44 stages; 428 offline tests per runtime/OS, zero offline skips; 19 deployed-archive comparisons per combination. Three six-node topology runs pass all 30 recovery/failure scenarios. P4D 2023.1 through 2026.1 on UBI 8/9 pass all 14 cells and 98 scenarios. Static, documentation and provenance checks pass. Containers do not certify every fleet kernel, patch build or integration. Tested source manifest SHA-256: e1a23e17b8ced8c1148d9a250589977c6f6e98606bf2a81dc3ff5d7c909e3695 Deployed comparison archive SHA-256: 1bd23217356ee3808e040d39aea5be9519bda7c8f2b4924656a70aff8ad7293a |