#!/bin/bash
# Fresh node provisioner. The host orchestrator creates only labelled disposable
# containers and copies this script and the reviewed source into /inputs.
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
[[ $EUID == 0 && -f /run/sdp-topology-test && -f /inputs/sdp/.export-manifest.json ]] || fail 'Missing disposable marker/source.'
[[ $(cat /proc/1/comm) == systemd && ! -e /p4 && ! -L /p4 ]] || fail 'Requires a fresh systemd container without /p4.'
NODE=${1:-} ROLE=${2:-} UPSTREAM=${3:-} PASSWORD=${4:-}
[[ $NODE =~ ^[a-z][a-z0-9-]+$ && $UPSTREAM =~ ^[a-z][a-z0-9-]+$ && $PASSWORD =~ ^[a-zA-Z0-9!]+$ ]] || fail 'Invalid fixture arguments.'
case $ROLE in master) TYPE=p4d_master;; edge) TYPE=p4d_edge;; standby) TYPE=p4d_standby;; edge-standby) TYPE=p4d_edgerep;; broker) TYPE=p4broker;; proxy) TYPE=p4proxy;; *) fail 'Unknown node role.';; esac
ROOT=/srv/sdp-test-topology
mkdir -p "$ROOT/p4" "$ROOT/meta1" "$ROOT/meta2" "$ROOT/data" "$ROOT/logs" "$ROOT/journals" /results /run/sshd || exit 1
ln -s "$ROOT/p4" /p4 || exit 1
cp -a /inputs/sdp "$ROOT/sdp" || exit 1
for binary in p4 p4d p4broker p4p; do
[[ -x /inputs/binaries/$binary ]] || fail "Missing checked binary $binary."
cp "/inputs/binaries/$binary" "$ROOT/sdp/Server/Unix/p4/common/bin/$binary" || exit 1
"/inputs/binaries/$binary" -V > "/results/$binary-version.txt" 2>&1 || exit 1
done
chown -R perforce:perforce "$ROOT/sdp" || exit 1
cp "$ROOT/sdp/Server/Unix/setup/template.mkdirs.cfg" "$ROOT/mkdirs.cfg" || exit 1
MASTER=$UPSTREAM
[[ $ROLE == master ]] && MASTER=$NODE
cat >> "$ROOT/mkdirs.cfg" <<CFG
SDP_INSTANCE=1
MASTERINSTANCE=1
DB1=${ROOT#/}/meta1
DB2=${ROOT#/}/meta2
DD=${ROOT#/}/data
CKP=\$DD
CN=\$DD
LG=${ROOT#/}/logs
JR=${ROOT#/}/journals
SDP=$ROOT/sdp
OSUSER=perforce
OSGROUP=perforce
CASE_SENSITIVE=1
P4_PORT=1666
P4BROKER_PORT=1666
P4COMMITSERVER=commit
P4DNSNAME=$UPSTREAM
MASTER_ID=$MASTER
REPLICA_ID=$NODE
SERVER_TYPE=$TYPE
P4ADMINPASS='$PASSWORD'
MAILTO=
MAILFROM=disposable@localhost
MAILHOST=localhost
CFG
chmod 600 "$ROOT/mkdirs.cfg" || exit 1
(cd "$ROOT/sdp/Server/Unix/setup" && ./mkdirs.sh "$ROOT/mkdirs.cfg") > /results/mkdirs.log 2>&1 || fail 'mkdirs failed; see /results/mkdirs.log.'
# Explicit identity for all roles, including broker/proxy installers.
printf '%s\n' "$NODE" > /p4/1/root/server.id || exit 1
printf '%s\n' "$TYPE" > /p4/1/root/sdp_server_type.txt || exit 1
if [[ ! -f /p4/common/config/$NODE.vars ]]; then
cp "/p4/common/config/$MASTER.vars" "/p4/common/config/$NODE.vars" || exit 1
fi
# Promotion changes server.id to the upstream ID. Keep its matching settings.
cp "/p4/common/config/$NODE.vars" "/p4/common/config/$NODE.vars.installed" || exit 1
if [[ $MASTER != "$NODE" ]]; then
cp "/p4/common/config/$NODE.vars" "/p4/common/config/$MASTER.vars" || exit 1
fi
cat >> /p4/common/config/p4_1.vars <<CFG
export P4PORT=127.0.0.1:1666
export P4SVRPORT=1666
export P4MASTERHOST=$UPSTREAM
export P4MASTERPORT=$UPSTREAM:1666
export P4JOURNAL=\$P4HOME/journals/journal
export P4D_FLAGS="-p \$P4SVRPORT -r \$P4ROOT -J \$P4JOURNAL -L \$P4LOG -q -d --pid-file"
export SDPMAIL=/bin/true
export SNAPSHOT_METHOD=off
export KEEPCKPS=2
export KEEPJNLS=1
export PROXY_PORT=1666
export PROXY_TARGET=commit:1666
CFG
chown -R perforce:perforce /p4/common/config /p4/1/root || exit 1
for daemon in p4d p4broker p4p; do
cat > "/etc/systemd/system/${daemon}_1.service" <<UNIT
[Unit]
Description=Disposable SDP topology $NODE $daemon
After=network.target
[Service]
Type=forking
User=perforce
Environment=OS_INIT_MECHANISM=systemd
ExecStart=/p4/1/bin/${daemon}_1_init start
ExecStop=/p4/1/bin/${daemon}_1_init stop
TimeoutStartSec=90
TimeoutStopSec=90
[Install]
WantedBy=multi-user.target
UNIT
if [[ $daemon == p4d ]]; then
sed -i '/Type=forking/a PIDFile=/p4/1/root/server.pid' "/etc/systemd/system/${daemon}_1.service" || exit 1
fi
done
printf 'perforce ALL=(root) NOPASSWD: /usr/bin/systemctl * p4d_1, /usr/bin/systemctl * p4d_1.service\n' > /etc/sudoers.d/sdp-topology-test || exit 1
chmod 440 /etc/sudoers.d/sdp-topology-test || exit 1
visudo -cf /etc/sudoers.d/sdp-topology-test || exit 1
systemctl daemon-reload || exit 1
systemctl enable p4d_1.service || exit 1
cp /inputs/sdp/.export-manifest.json /results/source-manifest.json || exit 1
sha256sum "$ROOT/mkdirs.cfg" > /results/mkdirs.cfg.sha256 || exit 1
# Preserve reviewable settings without leaking even disposable passwords.
sed '/^P4ADMINPASS=/s/=.*/=<generated disposable credential>/' "$ROOT/mkdirs.cfg" > /results/mkdirs.redacted.cfg
if [[ $ROLE == master ]]; then
systemctl start p4d_1.service || fail 'Initial server start failed.'
su - perforce -s /bin/bash -c 'source /p4/common/bin/p4_vars 1 || exit 1
password=$(cat "$SDP_ADMIN_PASSWORD_FILE") || exit 1
printf "%s\n%s\n" "$password" "$password" | "$P4BIN" passwd || exit 1
/p4/common/bin/p4login 1 || exit 1
/p4/sdp/Server/Unix/setup/configure_new_server.sh 1' > /results/configure.log 2>&1 || fail 'Commit configuration failed.'
fi
printf 'Node %s (%s) installed through mkdirs with independent storage.\n' "$NODE" "$ROLE"
| # | 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 |