mirror of
https://github.com/buckit-io/buckit.git
synced 2026-09-24 03:16:17 +00:00
139 lines
4.8 KiB
Bash
Executable File
139 lines
4.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Container entrypoint: create loopback XFS drives, then exec systemd.
|
|
set -e
|
|
|
|
DRIVES=${DRIVES:-4}
|
|
DRIVE_SIZE=${DRIVE_SIZE:-1G}
|
|
DATA_DIR=${DATA_DIR:-/data}
|
|
SSH_ROOT_PASSWORD=${SSH_ROOT_PASSWORD:-buckitadmin}
|
|
HDD_DELAY_MS=${HDD_DELAY_MS:-0}
|
|
NETEM_DELAY=${NETEM_DELAY:-0ms}
|
|
NETEM_JITTER=${NETEM_JITTER:-0ms}
|
|
|
|
if command -v chpasswd >/dev/null 2>&1; then
|
|
echo "root:${SSH_ROOT_PASSWORD}" | chpasswd
|
|
fi
|
|
|
|
mkdir -p /etc/buckit
|
|
{
|
|
printf 'MINIO_ROOT_USER=%q\n' "${MINIO_ROOT_USER:-buckitadmin}"
|
|
printf 'MINIO_ROOT_PASSWORD=%q\n' "${MINIO_ROOT_PASSWORD:-buckitadmin}"
|
|
printf 'MINIO_CONFIG_ENV_FILE=\n'
|
|
printf 'BUCKIT_FAST_GET=%q\n' "${BUCKIT_FAST_GET:-0}"
|
|
printf 'BUCKIT_ENDPOINTS=%q\n' "${BUCKIT_ENDPOINTS:-}"
|
|
printf 'MINIO_STORAGE_CLASS_STANDARD=%q\n' "${MINIO_STORAGE_CLASS_STANDARD:-}"
|
|
printf 'MINIO_PROMETHEUS_AUTH_TYPE=%q\n' "${MINIO_PROMETHEUS_AUTH_TYPE:-public}"
|
|
} >/etc/buckit/buckit.env
|
|
chmod 0600 /etc/buckit/buckit.env
|
|
|
|
# Ensure loop device support
|
|
modprobe loop 2>/dev/null || true
|
|
if [ "$HDD_DELAY_MS" -gt 0 ] 2>/dev/null; then
|
|
modprobe dm_mod 2>/dev/null || true
|
|
modprobe dm_delay 2>/dev/null || true
|
|
if ! dmsetup targets 2>/dev/null | awk '{print $1}' | grep -qx 'delay'; then
|
|
echo "HDD_DELAY_MS=${HDD_DELAY_MS} requested, but the Linux kernel in this container does not expose the device-mapper delay target." >&2
|
|
echo "Run the Docker rig on a native Linux host with dm-delay support, or disable HDD_DELAY_MS." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [ "$NETEM_DELAY" != "0ms" ] && [ "$NETEM_DELAY" != "0us" ] && [ "$NETEM_DELAY" != "0" ]; then
|
|
modprobe sch_netem 2>/dev/null || true
|
|
netem_args="delay ${NETEM_DELAY}"
|
|
if [ "$NETEM_JITTER" != "0ms" ] && [ "$NETEM_JITTER" != "0us" ] && [ "$NETEM_JITTER" != "0" ]; then
|
|
netem_args="${netem_args} ${NETEM_JITTER} distribution normal"
|
|
fi
|
|
if ! tc qdisc add dev eth0 root netem ${netem_args}; then
|
|
echo "NETEM_DELAY=${NETEM_DELAY} NETEM_JITTER=${NETEM_JITTER} requested, but tc netem could not be applied on eth0." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Pre-populate /dev/loop0.../dev/loop63 so that losetup --find --show can open
|
|
# the device node immediately after the kernel atomically allocates its index.
|
|
# Without this a freshly-started container may be missing higher-numbered nodes.
|
|
for _n in $(seq 0 63); do
|
|
[ -e "/dev/loop${_n}" ] || mknod "/dev/loop${_n}" b 7 "${_n}" 2>/dev/null || true
|
|
done
|
|
unset _n
|
|
|
|
# Detach any orphaned loop devices pointing to deleted backing files (e.g. from
|
|
# prior container runs on Docker Desktop, whose loop attachments leak into the
|
|
# host VM kernel even after the container's volume is removed).
|
|
if command -v losetup >/dev/null 2>&1; then
|
|
losetup -a 2>/dev/null | awk -F: '/\(deleted\)/{print $1}' | while read -r stale; do
|
|
losetup -d "$stale" 2>/dev/null || true
|
|
done
|
|
fi
|
|
|
|
attach_loop() {
|
|
# losetup --find --show atomically selects the next free loop device and
|
|
# attaches the image in one kernel operation (LOOP_CTL_GET_FREE +
|
|
# LOOP_SET_FD). The old two-step approach — losetup -f to read the next
|
|
# free device, then a separate losetup <dev> <img> to attach — has a TOCTOU
|
|
# race: when N nodes start concurrently they all observe the same "next
|
|
# free" number before any of them has attached, so N-1 of them hit
|
|
# "Device or resource busy". The atomic form closes that window entirely.
|
|
local img="$1"
|
|
local loop num
|
|
loop=$(losetup --find --show "$img") || return 1
|
|
# Materialise the /dev node if the kernel assigned an index above the
|
|
# pre-populated range inside this container's /dev namespace.
|
|
num=${loop##/dev/loop}
|
|
[ -e "$loop" ] || mknod "$loop" b 7 "$num" 2>/dev/null || true
|
|
echo "$loop"
|
|
}
|
|
|
|
create_delayed_device() {
|
|
local backing="$1"
|
|
local delay_ms="$2"
|
|
local mapper_name="$3"
|
|
local sectors table
|
|
|
|
sectors=$(blockdev --getsz "$backing")
|
|
if [ -z "$sectors" ]; then
|
|
echo "failed to determine size for $backing" >&2
|
|
return 1
|
|
fi
|
|
|
|
dmsetup remove "$mapper_name" 2>/dev/null || true
|
|
table="0 ${sectors} delay ${backing} 0 ${delay_ms}"
|
|
dmsetup create "$mapper_name" --table "$table"
|
|
dmsetup mknodes "$mapper_name" 2>/dev/null || true
|
|
for _try in $(seq 1 20); do
|
|
if [ -e "/dev/mapper/${mapper_name}" ]; then
|
|
break
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
if [ ! -e "/dev/mapper/${mapper_name}" ]; then
|
|
echo "failed to materialize /dev/mapper/${mapper_name}" >&2
|
|
return 1
|
|
fi
|
|
echo "/dev/mapper/${mapper_name}"
|
|
}
|
|
|
|
for i in $(seq 0 $((DRIVES - 1))); do
|
|
img="/var/lib/buckit-drives/drive${i}.img"
|
|
mnt="${DATA_DIR}/drive${i}"
|
|
mapper_name="buckit-delay-$(hostname)-${i}"
|
|
mkdir -p "$mnt"
|
|
is_new=0
|
|
if [ ! -f "$img" ]; then
|
|
fallocate -l "$DRIVE_SIZE" "$img"
|
|
is_new=1
|
|
fi
|
|
loop=$(attach_loop "$img")
|
|
device="$loop"
|
|
if [ "$HDD_DELAY_MS" -gt 0 ] 2>/dev/null; then
|
|
device=$(create_delayed_device "$loop" "$HDD_DELAY_MS" "$mapper_name")
|
|
fi
|
|
if [ "$is_new" -eq 1 ]; then
|
|
mkfs.xfs -f "$device"
|
|
fi
|
|
mount "$device" "$mnt"
|
|
done
|
|
|
|
exec /sbin/init
|