mirror of
https://github.com/langgenius/dify.git
synced 2026-11-11 22:02:23 +08:00
177 lines
8.4 KiB
Bash
Executable File
177 lines
8.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Provision only an ephemeral normal CI VM. This is not a deployment installer.
|
|
set -euo pipefail
|
|
umask 077
|
|
|
|
[[ ${GITHUB_ACTIONS:-} == true && -n ${RUNNER_TEMP:-} && -n ${GITHUB_RUN_ID:-} ]] || {
|
|
echo 'This fixture requires an ephemeral GitHub Actions runner.' >&2
|
|
exit 1
|
|
}
|
|
unit=dify-native.service
|
|
marker=/run/dify-native-ci-owner
|
|
owner="${GITHUB_RUN_ID}:${GITHUB_RUN_ATTEMPT:-1}"
|
|
|
|
owned_fixture() {
|
|
[[ $(sudo cat "$marker" 2>/dev/null) == "$owner" ]]
|
|
}
|
|
|
|
require_manager_subgroup_support() {
|
|
local native_systemd_version
|
|
native_systemd_version=$(systemctl show --property=Version --value)
|
|
[[ $native_systemd_version =~ ^([0-9]+) ]]
|
|
(( BASH_REMATCH[1] >= 254 )) || {
|
|
echo 'Native host profile requires systemd 254 or newer.' >&2
|
|
return 1
|
|
}
|
|
}
|
|
|
|
assert_manager_subgroup() {
|
|
local native_manager_pid native_cgroup_root
|
|
native_cgroup_root=/sys/fs/cgroup/system.slice/dify-native.service
|
|
[[ $(systemctl show "$unit" --property=DelegateSubgroup --value) == manager ]]
|
|
native_manager_pid=$(systemctl show "$unit" --property=MainPID --value)
|
|
[[ $native_manager_pid =~ ^[0-9]+$ && $native_manager_pid -gt 1 ]]
|
|
[[ -z $(sudo cat "$native_cgroup_root/cgroup.procs") ]]
|
|
sudo grep -Fxq "$native_manager_pid" "$native_cgroup_root/manager/cgroup.procs"
|
|
}
|
|
|
|
case ${1:-} in
|
|
provision)
|
|
binary_dir=${2:?binary directory required}
|
|
profile_dir=${3:?profile directory required}
|
|
[[ $(cat /proc/1/comm) == systemd ]]
|
|
require_manager_subgroup_support
|
|
[[ $(stat -fc %T /sys/fs/cgroup) == cgroup2fs ]]
|
|
[[ $(cat /sys/module/apparmor/parameters/enabled) == Y ]]
|
|
for path in /var/lib/dify-native /home/dify /workspace "$marker"; do
|
|
[[ ! -e $path ]] || { echo "Fixture path already exists: $path" >&2; exit 1; }
|
|
done
|
|
if id dify-native >/dev/null 2>&1; then
|
|
echo 'Fixture account already exists.' >&2
|
|
exit 1
|
|
fi
|
|
for binary in shellctl shellctl-runner shellctl-runner-exit shellctl-sanitize-pty; do
|
|
[[ -x "$binary_dir/$binary" ]]
|
|
done
|
|
for profile in native-test-supervisor.service native-test-start.sh native-test-apparmor.profile; do
|
|
[[ -s "$profile_dir/$profile" ]]
|
|
done
|
|
printf '%s\n' "$owner" | sudo tee "$marker" >/dev/null
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends apparmor apparmor-utils tmux iproute2
|
|
sudo useradd --system --no-create-home --home-dir /nonexistent --shell /usr/sbin/nologin dify-native
|
|
sudo install -d -m 0700 -o dify-native -g dify-native /var/lib/dify-native
|
|
sudo install -d -m 0555 /var/lib/dify-native/snapshots/fixture-home /var/lib/dify-native/inputs
|
|
sudo install -d -m 0755 /home/dify /workspace /etc/dify-native /usr/local/libexec
|
|
printf 'synthetic native input\n' | sudo tee /var/lib/dify-native/inputs/fixture-input >/dev/null
|
|
sudo chmod 0444 /var/lib/dify-native/inputs/fixture-input
|
|
printf 'foreign scope canary\n' | sudo tee /var/lib/dify-native/foreign-canary >/dev/null
|
|
sudo chmod 0400 /var/lib/dify-native/foreign-canary
|
|
for binary in shellctl shellctl-runner shellctl-runner-exit shellctl-sanitize-pty; do
|
|
sudo install -m 0755 "$binary_dir/$binary" "/usr/local/bin/$binary"
|
|
done
|
|
sudo install -m 0755 "$profile_dir/native-test-start.sh" /usr/local/libexec/dify-native-start
|
|
sudo install -m 0644 "$profile_dir/native-test-supervisor.service" "/etc/systemd/system/$unit"
|
|
sudo install -m 0644 "$profile_dir/native-test-apparmor.profile" /etc/apparmor.d/dify-native-test-supervisor
|
|
# This fixture targets Ubuntu 24.04. Grant userns to this profile only.
|
|
. /etc/os-release
|
|
[[ $ID == ubuntu && $VERSION_ID == 24.04 ]]
|
|
printf 'userns,\n' | sudo tee /etc/apparmor.d/dify-native-userns >/dev/null
|
|
sudo chmod 0644 /etc/apparmor.d/dify-native-userns
|
|
sudo apparmor_parser --replace /etc/apparmor.d/dify-native-test-supervisor
|
|
sudo systemd-analyze verify "/etc/systemd/system/$unit"
|
|
token=$(openssl rand -hex 32)
|
|
printf '::add-mask::%s\n' "$token"
|
|
private_env=$(mktemp "$RUNNER_TEMP/native-supervisor-env.XXXXXX")
|
|
trap 'rm -f "$private_env"' EXIT
|
|
printf 'SHELLCTL_AUTH_TOKEN=%s\nSHELLCTL_NATIVE_MEDIATORS=127.0.0.1:18081\n' "$token" > "$private_env"
|
|
sudo install -m 0600 "$private_env" /etc/dify-native/supervisor.env
|
|
printf 'SHELLCTL_NATIVE_TEST_TOKEN=%s\nSHELLCTL_NATIVE_TEST_URL=http://127.0.0.1:5005\nSHELLCTL_NATIVE_CANARY_URL=http://127.0.0.1:18081\nSHELLCTL_NATIVE_RESTART_STATE=%s/native-scope-restart.json\n' "$token" "$RUNNER_TEMP" >> "$GITHUB_ENV"
|
|
sudo systemd-run --unit=dify-native-canary --property="User=$(id -un)" \
|
|
--property=NoNewPrivileges=yes --property=MemoryMax=64M --property=TasksMax=16 \
|
|
--property=CPUQuota=25% --property=RestrictAddressFamilies=AF_INET \
|
|
/usr/bin/python3 "$GITHUB_WORKSPACE/.github/scripts/builder-native-runtime-canary.py"
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl start "$unit"
|
|
for _ in $(seq 1 30); do
|
|
if printf 'header = "Authorization: Bearer %s"\n' "$token" | curl --fail --silent --config - http://127.0.0.1:5005/healthz >/dev/null; then
|
|
assert_manager_subgroup
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo 'Native supervisor did not become ready.' >&2
|
|
exit 1
|
|
;;
|
|
mount-test)
|
|
owned_fixture
|
|
binary_dir=${2:?binary directory required}
|
|
[[ -x "$binary_dir/testscope-kernel.test" ]]
|
|
native_mount_tmp=$(mktemp -d /tmp/dify-native-mount-ci.XXXXXX)
|
|
trap 'sudo rmdir "$native_mount_tmp" || true' EXIT
|
|
sudo chown root:root "$native_mount_tmp"
|
|
sudo chmod 0755 "$native_mount_tmp"
|
|
sudo install -m 0755 "$binary_dir/testscope-kernel.test" /usr/local/libexec/dify-native-mount-test
|
|
timeout --kill-after=5s 90s sudo env -i \
|
|
PATH=/usr/local/bin:/usr/bin:/bin LANG=C TMPDIR="$native_mount_tmp" \
|
|
NATIVE_MOUNT_TEST_UID="$(id -u dify-native)" NATIVE_MOUNT_TEST_GID="$(id -g dify-native)" \
|
|
/usr/local/libexec/dify-native-mount-test \
|
|
-test.run '^TestReadonlyBindInheritedMounts$' -test.v=test2json -test.timeout=75s
|
|
;;
|
|
restart)
|
|
owned_fixture
|
|
require_manager_subgroup_support
|
|
sudo systemctl restart "$unit"
|
|
for _ in $(seq 1 30); do
|
|
if printf 'header = "Authorization: Bearer %s"\n' "${SHELLCTL_NATIVE_TEST_TOKEN:?native supervisor token required}" | curl --fail --silent --config - http://127.0.0.1:5005/healthz >/dev/null; then
|
|
assert_manager_subgroup
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo 'Native supervisor did not recover after restart.' >&2
|
|
exit 1
|
|
;;
|
|
logs)
|
|
owned_fixture || exit 0
|
|
uname -a
|
|
systemctl --version
|
|
systemctl show --property=Version
|
|
sudo systemctl show "$unit" --property=User,Group,Delegate,DelegateSubgroup,CPUQuotaPerSecUSec,MemoryMax,TasksMax,NoNewPrivileges,AppArmorProfile,ControlGroup,SystemCallFilter,SystemCallErrorNumber
|
|
# Inspect inherited image submounts without dumping mount sources/options,
|
|
# process arguments, environment, or service credentials.
|
|
native_manager_pid=$(systemctl show "$unit" --property=MainPID --value) || native_manager_pid=
|
|
if [[ $native_manager_pid =~ ^[0-9]+$ && $native_manager_pid -gt 1 ]]; then
|
|
printf 'Native manager image mount points and VFS flags\n'
|
|
sudo awk '$5 ~ /^\/(usr|bin|sbin|lib|lib64|etc\/ssl)(\/|$)/ && count++ < 128 {
|
|
print $5, $6
|
|
}' "/proc/$native_manager_pid/mountinfo" || true
|
|
fi
|
|
for group in /sys/fs/cgroup/system.slice/dify-native.service{,/manager,/scopes}; do
|
|
for file in cgroup.type cgroup.procs cgroup.subtree_control cgroup.events; do
|
|
printf '%s\n' "$group/$file"
|
|
sudo cat "$group/$file" || true
|
|
done
|
|
done
|
|
sudo journalctl --no-pager -u "$unit" -n 300
|
|
# A denied startup can have no matching kernel records. Keep the remaining
|
|
# diagnostic output; this best-effort dump never decides qualification.
|
|
sudo journalctl --dmesg --no-pager --grep=dify-native -n 100 || true
|
|
sudo aa-status --json
|
|
;;
|
|
cleanup)
|
|
owned_fixture || exit 0
|
|
# KillMode=process deliberately retains workers across manager restarts.
|
|
# CI teardown instead kills the complete dedicated unit, never host siblings.
|
|
sudo systemctl kill --kill-whom=all --signal=KILL "$unit" || true
|
|
sudo systemctl stop "$unit" || true
|
|
sudo systemctl reset-failed "$unit" || true
|
|
sudo systemctl stop dify-native-canary.service || true
|
|
sudo rm -f /etc/dify-native/supervisor.env "$marker"
|
|
;;
|
|
*)
|
|
echo 'Usage: builder-native-runtime-ci.sh provision BIN_DIR PROFILE_DIR | mount-test BIN_DIR | restart | logs | cleanup' >&2
|
|
exit 2
|
|
;;
|
|
esac
|