mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-11-06 01:13:39 +01:00
When no intercept mode is selected by flag or config, a new installation on macOS or a Windows desktop edition now uses "dns". Windows Server, iOS and other platforms keep interface DNS mode unless intercept is selected explicitly, and so does a config that cannot run in intercept mode. Existing Standard installations are not switched on upgrade. ctrld start marks the services it installs fresh, and the service applies the default below service.intercept_mode against the config it fetches, so a service with neither a mode nor the marker predates the default and keeps interface DNS mode, whichever path upgrades it; the macOS package also pins such a service to "off" explicitly. An explicit "off" stays final and is persisted.
212 lines
6.7 KiB
Bash
Executable File
212 lines
6.7 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)
|
|
postinstall_source="$repo_root/scripts/pkg/postinstall"
|
|
fixture=$(mktemp -d "${TMPDIR:-/tmp}/ctrld-pkg-intercept.XXXXXX")
|
|
trap 'rm -rf "$fixture"' EXIT HUP INT TERM
|
|
|
|
bin="$fixture/bin"
|
|
mkdir -p "$bin"
|
|
|
|
cat >"$bin/defaults" <<'EOF'
|
|
#!/bin/sh
|
|
key=${3:-}
|
|
case "$key" in
|
|
ProvisionToken)
|
|
[ "${FAKE_TOKEN_PRESENT:-0}" = "1" ] || exit 1
|
|
printf '%s\n' "${FAKE_TOKEN:-test-token}"
|
|
;;
|
|
InterceptMode)
|
|
[ "${FAKE_MODE_PRESENT:-0}" = "1" ] || exit 1
|
|
printf '%s\n' "${FAKE_MODE:-}"
|
|
;;
|
|
ProgramArguments)
|
|
[ "${FAKE_ARGS_PRESENT:-0}" = "1" ] || exit 1
|
|
printf '%s\n' "${FAKE_ARGS:-}"
|
|
;;
|
|
CustomHostname|UseDevEnvironment)
|
|
exit 1
|
|
;;
|
|
*)
|
|
exit 1
|
|
;;
|
|
esac
|
|
EOF
|
|
|
|
cat >"$bin/launchctl" <<'EOF'
|
|
#!/bin/sh
|
|
printf 'launchctl %s\n' "$*" >>"$CALLS"
|
|
exit 0
|
|
EOF
|
|
|
|
cat >"$bin/ctrld" <<'EOF'
|
|
#!/bin/sh
|
|
printf 'ctrld %s\n' "$*" >>"$CALLS"
|
|
if [ "${FAKE_CTRLD_EXIT:-0}" = "0" ]; then
|
|
case " $* " in
|
|
*" --cd-org="*) : >"$FAKE_PLIST" ;;
|
|
esac
|
|
fi
|
|
exit "${FAKE_CTRLD_EXIT:-0}"
|
|
EOF
|
|
|
|
chmod +x "$bin/defaults" "$bin/launchctl" "$bin/ctrld"
|
|
|
|
assert_contains() {
|
|
expected=$1
|
|
file=$2
|
|
if ! grep -Fq -- "$expected" "$file"; then
|
|
printf 'FAIL: expected %s in %s\n' "$expected" "$file" >&2
|
|
sed -n '1,120p' "$file" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
assert_not_contains() {
|
|
unexpected=$1
|
|
file=$2
|
|
if grep -Fq -- "$unexpected" "$file"; then
|
|
printf 'FAIL: did not expect %s in %s\n' "$unexpected" "$file" >&2
|
|
sed -n '1,120p' "$file" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
run_case() {
|
|
name=$1
|
|
existing=$2
|
|
mode_present=$3
|
|
mode=$4
|
|
ctrld_exit=${5:-0}
|
|
expected_status=${6:-0}
|
|
case_dir="$fixture/$name"
|
|
mkdir -p "$case_dir"
|
|
plist="$case_dir/ctrld.plist"
|
|
prefs="$case_dir/preferences"
|
|
calls="$case_dir/calls"
|
|
output="$case_dir/output"
|
|
postinstall="$case_dir/postinstall"
|
|
case_tmpdir="$case_dir/tmp"
|
|
mkdir -p "$case_tmpdir"
|
|
: >"$calls"
|
|
if [ "$existing" = "1" ]; then
|
|
: >"$plist"
|
|
fi
|
|
|
|
sed \
|
|
-e "s|^PLIST=\"/Library/LaunchDaemons/ctrld.plist\"$|PLIST=\"$plist\"|" \
|
|
-e "s|^CTRLD=\"/usr/local/bin/ctrld\"$|CTRLD=\"$bin/ctrld\"|" \
|
|
-e "s|^PREFS=\"/Library/Managed Preferences/com.controld.ctrld\"$|PREFS=\"$prefs\"|" \
|
|
"$postinstall_source" >"$postinstall"
|
|
chmod +x "$postinstall"
|
|
|
|
status=0
|
|
PATH="$bin:$PATH" \
|
|
TMPDIR="$case_tmpdir" \
|
|
CALLS="$calls" \
|
|
FAKE_TOKEN_PRESENT=1 \
|
|
FAKE_TOKEN=test-token \
|
|
FAKE_MODE_PRESENT="$mode_present" \
|
|
FAKE_MODE="$mode" \
|
|
FAKE_CTRLD_EXIT="$ctrld_exit" \
|
|
FAKE_PLIST="$plist" \
|
|
"$postinstall" >"$output" 2>&1 || status=$?
|
|
|
|
if [ "$status" -ne "$expected_status" ]; then
|
|
printf 'FAIL: %s exited %s, want %s\n' "$name" "$status" "$expected_status" >&2
|
|
sed -n '1,120p' "$output" >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf '%s\n' "$case_dir"
|
|
}
|
|
|
|
assert_not_contains 'CTRLD_POSTINSTALL_' "$postinstall_source"
|
|
|
|
case_dir=$(run_case fresh-legacy 0 0 '')
|
|
assert_contains 'ctrld start --cd-org=test-token' "$case_dir/calls"
|
|
assert_not_contains '--intercept-mode' "$case_dir/calls"
|
|
|
|
# Standard must be selected explicitly: with no flag, ctrld defaults to dns
|
|
# intercept mode on macOS.
|
|
case_dir=$(run_case fresh-standard 0 1 standard)
|
|
assert_contains 'ctrld start --cd-org=test-token --intercept-mode off' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case fresh-intercept 0 1 intercept-dns)
|
|
assert_contains 'ctrld start --cd-org=test-token --intercept-mode dns' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case upgrade-legacy 1 0 '')
|
|
assert_contains 'launchctl load' "$case_dir/calls"
|
|
assert_not_contains 'ctrld start' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case upgrade-standard 1 1 standard)
|
|
assert_contains 'ctrld start --intercept-mode off' "$case_dir/calls"
|
|
assert_not_contains 'launchctl load' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case upgrade-intercept 1 1 intercept-dns)
|
|
assert_contains 'ctrld start --intercept-mode dns' "$case_dir/calls"
|
|
assert_not_contains 'launchctl load' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case fresh-invalid 0 1 invalid 0 1)
|
|
assert_contains 'ERROR: provisioning failed: stage=package code=INTERCEPT_MODE_INVALID (exit 1)' "$case_dir/output"
|
|
assert_not_contains 'ctrld start' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case upgrade-invalid 1 1 invalid)
|
|
assert_contains 'WARNING: unsupported InterceptMode in managed preferences; preserving existing service mode' "$case_dir/output"
|
|
assert_contains 'launchctl load' "$case_dir/calls"
|
|
assert_not_contains 'ctrld start' "$case_dir/calls"
|
|
|
|
case_dir=$(run_case upgrade-standard-failure 1 1 standard 1 1)
|
|
assert_contains 'ctrld start --intercept-mode off' "$case_dir/calls"
|
|
assert_contains 'ERROR: upgrade installed but managed InterceptMode could not be applied' "$case_dir/output"
|
|
assert_not_contains 'launchctl load' "$case_dir/calls"
|
|
|
|
# The legacy upgrade case (#589): a profile without InterceptMode upgrading a
|
|
# service installed in Standard mode before ctrld defaulted to dns intercept
|
|
# mode. Its arguments select no mode, so it is pinned to Standard
|
|
# (--intercept-mode off) instead of switching to the new default.
|
|
legacy_args='(
|
|
"/usr/local/bin/ctrld",
|
|
run,
|
|
"--cd=abc123",
|
|
"--config=/etc/controld/ctrld.toml"
|
|
)'
|
|
export FAKE_ARGS_PRESENT=1 FAKE_ARGS="$legacy_args"
|
|
case_dir=$(run_case upgrade-legacy-standard 1 0 '')
|
|
assert_contains 'existing service selects no intercept mode; keeping Standard mode' "$case_dir/output"
|
|
assert_contains 'ctrld start --intercept-mode off' "$case_dir/calls"
|
|
assert_not_contains 'launchctl load' "$case_dir/calls"
|
|
|
|
# An invalid profile value warns, and still keeps a legacy service in Standard.
|
|
case_dir=$(run_case upgrade-invalid-legacy-standard 1 1 invalid)
|
|
assert_contains 'WARNING: unsupported InterceptMode in managed preferences; preserving existing service mode' "$case_dir/output"
|
|
assert_contains 'ctrld start --intercept-mode off' "$case_dir/calls"
|
|
|
|
# A service whose arguments already select a mode keeps it unchanged.
|
|
export FAKE_ARGS='(
|
|
"/usr/local/bin/ctrld",
|
|
run,
|
|
"--cd=abc123",
|
|
"--intercept-mode",
|
|
dns
|
|
)'
|
|
case_dir=$(run_case upgrade-legacy-intercept 1 0 '')
|
|
assert_contains 'launchctl load' "$case_dir/calls"
|
|
assert_not_contains 'ctrld start' "$case_dir/calls"
|
|
|
|
# A service a fresh install marked for the default keeps the marker, and the
|
|
# new binary applies the default to it.
|
|
export FAKE_ARGS='(
|
|
"/usr/local/bin/ctrld",
|
|
run,
|
|
"--cd=abc123",
|
|
"--intercept-mode-default=true"
|
|
)'
|
|
case_dir=$(run_case upgrade-default-marker 1 0 '')
|
|
assert_contains 'launchctl load' "$case_dir/calls"
|
|
assert_not_contains 'ctrld start' "$case_dir/calls"
|
|
unset FAKE_ARGS_PRESENT FAKE_ARGS
|
|
|
|
printf 'PASS: pkg postinstall preserves the existing service mode and applies standard/intercept-dns policy\n'
|