HH-682: fix candidate volume ownership
Docker image / Test (pull_request) Failing after 1m10s
Docker image / Build and publish (pull_request) Skipped

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-08-26 12:16:29 +08:00
co-authored by multica-agent
parent 2363afba9b
commit 9b5d351068
2 changed files with 63 additions and 16 deletions
+2 -2
View File
@@ -68,8 +68,8 @@ FROM alpine:${ALPINE_VERSION}
RUN apk add --no-cache ca-certificates curl gzip tzdata \
&& addgroup -S ssclash \
&& adduser -S -G ssclash -h /opt/clash ssclash \
&& mkdir -p /opt/clash /tmp/ssclash /usr/local/lib/ssclash /usr/local/share/ssclash \
&& chown -R ssclash:ssclash /opt/clash /tmp/ssclash
&& mkdir -p /data /opt/clash /tmp/ssclash /usr/local/lib/ssclash /usr/local/share/ssclash \
&& chown -R ssclash:ssclash /data /opt/clash /tmp/ssclash
COPY --from=bootstrap-builder /out/bootstrap /usr/local/bin/bootstrap
COPY --from=release-assets /assets/ssclash /usr/local/bin/ssclash
COPY --from=release-assets /assets/mihomo /usr/local/lib/ssclash/clash
+61 -14
View File
@@ -11,18 +11,21 @@ network="mohomo-network-smoke-${suffix}"
legacy_network="mohomo-legacy-network-smoke-${suffix}"
volume="mohomo-volume-smoke-${suffix}"
legacy_volume="mohomo-legacy-volume-smoke-${suffix}"
candidate_volume="mohomo-candidate-volume-smoke-${suffix}"
candidate_secret_file=$(mktemp "${TMPDIR:-/tmp}/mohomo-candidate-secret.XXXXXX")
secret="container-smoke-secret"
admin_password="container-smoke-admin-password"
case "$container:$unconfigured:$legacy_container:$provider:$network:$legacy_network:$volume:$legacy_volume" in
mohomo-docker-smoke-*':mohomo-docker-unconfigured-'*':mohomo-docker-legacy-'*':mohomo-provider-smoke-'*':mohomo-network-smoke-'*':mohomo-legacy-network-smoke-'*':mohomo-volume-smoke-'*':mohomo-legacy-volume-smoke-'*) ;;
case "$container:$unconfigured:$legacy_container:$provider:$network:$legacy_network:$volume:$legacy_volume:$candidate_volume" in
mohomo-docker-smoke-*':mohomo-docker-unconfigured-'*':mohomo-docker-legacy-'*':mohomo-provider-smoke-'*':mohomo-network-smoke-'*':mohomo-legacy-network-smoke-'*':mohomo-volume-smoke-'*':mohomo-legacy-volume-smoke-'*':mohomo-candidate-volume-smoke-'*) ;;
*) echo "refusing unsafe cleanup targets" >&2; exit 1 ;;
esac
cleanup() {
docker container rm --force "$container" "$unconfigured" "$legacy_container" "$provider" >/dev/null 2>&1 || true
docker volume rm "$volume" "$legacy_volume" >/dev/null 2>&1 || true
docker volume rm "$volume" "$legacy_volume" "$candidate_volume" >/dev/null 2>&1 || true
docker network rm "$network" "$legacy_network" >/dev/null 2>&1 || true
rm -f "$candidate_secret_file"
}
trap cleanup EXIT INT TERM
@@ -40,6 +43,19 @@ wait_for_health() {
done
}
wait_for_subscription() {
expected=$1
attempt=0
until docker exec "$provider" wget -qO- http://127.0.0.1:8080/provider.yaml | grep -F "$expected" >/dev/null; do
attempt=$((attempt + 1))
if [ "$attempt" -ge 10 ]; then
echo "subscription fixture did not serve expected content" >&2
exit 1
fi
sleep 1
done
}
assert_published_ports() {
published=$(docker port "$container")
for port in 7890/tcp 7890/udp 9091/tcp; do
@@ -124,17 +140,48 @@ docker run --detach --rm \
--name "$provider" \
--network "$network" \
--entrypoint /bin/sh \
"$image" -c 'while :; do printf "HTTP/1.1 200 OK\r\nContent-Type: text/yaml\r\nConnection: close\r\n\r\nproxies:\n - name: smoke-node\n type: socks5\n server: 127.0.0.1\n port: 9\n" | nc -l -p 8080; done' >/dev/null
"$image" -c 'while :; do
if [ -f /tmp/invalid-subscription ]; then
printf "HTTP/1.1 200 OK\r\nContent-Type: text/yaml\r\nConnection: close\r\n\r\nproxies: ["
else
printf "HTTP/1.1 200 OK\r\nContent-Type: text/yaml\r\nConnection: close\r\n\r\nproxies:\n - name: smoke-node\n type: socks5\n server: 127.0.0.1\n port: 9\n"
fi | nc -l -p 8080
done' >/dev/null
docker network connect "$legacy_network" "$provider"
attempt=0
until docker exec "$provider" wget -qO- http://127.0.0.1:8080/provider.yaml | grep -F 'name: smoke-node' >/dev/null; do
attempt=$((attempt + 1))
if [ "$attempt" -ge 10 ]; then
echo "subscription fixture did not become ready" >&2
exit 1
fi
sleep 1
done
wait_for_subscription 'name: smoke-node'
printf 'http://%s:8080/provider.yaml?token=%s\n' "$provider" "$secret" > "$candidate_secret_file"
chmod 0444 "$candidate_secret_file"
docker volume create "$candidate_volume" >/dev/null
docker run --rm \
--network "$network" \
--volume "$candidate_volume:/data" \
--mount "type=bind,source=${candidate_secret_file},target=/run/secrets/subscription,readonly" \
"$image" candidate >/dev/null
last_good=$(docker run --rm \
--volume "$candidate_volume:/data" \
--entrypoint /bin/sh \
"$image" -c 'test -w /data; test -L /data/last-good; grep -F "name: smoke-node" /data/last-good/subscription.yaml >/dev/null; readlink /data/last-good')
docker exec "$provider" touch /tmp/invalid-subscription
wait_for_subscription 'proxies: ['
if failure=$(docker run --rm \
--network "$network" \
--volume "$candidate_volume:/data" \
--mount "type=bind,source=${candidate_secret_file},target=/run/secrets/subscription,readonly" \
"$image" candidate 2>&1); then
echo "candidate accepted invalid YAML" >&2
exit 1
fi
if printf '%s\n' "$failure" | grep -F "$secret" >/dev/null; then
echo "candidate failure leaked subscription secret" >&2
exit 1
fi
docker run --rm \
--volume "$candidate_volume:/data" \
--entrypoint /bin/sh \
"$image" -c "test \"\$(readlink /data/last-good)\" = '$last_good'; grep -F 'name: smoke-node' /data/last-good/subscription.yaml >/dev/null"
docker exec "$provider" rm /tmp/invalid-subscription
wait_for_subscription 'name: smoke-node'
docker volume create "$legacy_volume" >/dev/null
docker run --rm \
@@ -287,4 +334,4 @@ assert_published_ports
web_port=$(docker port "$container" 9091/tcp | awk -F: 'NR == 1 { print $NF }')
assert_web_login_and_start "$web_port"
echo "container smoke test passed: legacy config migrates and validates a synthetic subscription offline; fresh volume fails closed; authenticated 9091 survives same-volume rebuild; only 7890/9091 are published"
echo "container smoke test passed: fresh candidate volume publishes and rolls back invalid YAML; legacy config migrates; fresh SSClash volume fails closed; authenticated 9091 survives same-volume rebuild; only 7890/9091 are published"