mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
test: resolve: add integration tests for browsing services
Co-authored-by: Frantisek Sumsal <frantisek@sumsal.cz> Co-authored-by: Vishwanath Chandapur <vishwanath.chandapur@philips.com>
This commit is contained in:
7
test/integration-tests/TEST-89-RESOLVED-MDNS/meson.build
Normal file
7
test/integration-tests/TEST-89-RESOLVED-MDNS/meson.build
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
integration_tests += [
|
||||
integration_test_template + {
|
||||
'name' : 'TEST-89-RESOLVED-MDNS',
|
||||
},
|
||||
]
|
||||
@@ -101,6 +101,7 @@ foreach dirname : [
|
||||
'TEST-86-MULTI-PROFILE-UKI',
|
||||
'TEST-87-AUX-UTILS-VM',
|
||||
'TEST-88-UPGRADE',
|
||||
'TEST-89-RESOLVED-MDNS',
|
||||
]
|
||||
subdir(dirname)
|
||||
endforeach
|
||||
|
||||
8
test/units/TEST-89-RESOLVED-MDNS.service
Normal file
8
test/units/TEST-89-RESOLVED-MDNS.service
Normal file
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Unit]
|
||||
Description=TEST-89-RESOLVED-MDNS
|
||||
|
||||
[Service]
|
||||
ExecStartPre=rm -f /failed /testok
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Type=oneshot
|
||||
254
test/units/TEST-89-RESOLVED-MDNS.sh
Executable file
254
test/units/TEST-89-RESOLVED-MDNS.sh
Executable file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/test-control.sh
|
||||
. "$(dirname "$0")"/test-control.sh
|
||||
|
||||
SERVICE_TYPE_COUNT=10
|
||||
SERVICE_COUNT=20
|
||||
CONTAINER_ZONE="test-$RANDOM"
|
||||
CONTAINER_1="test-mdns-1"
|
||||
CONTAINER_2="test-mdns-2"
|
||||
|
||||
# Prepare containers
|
||||
create_container() {
|
||||
local container="${1:?}"
|
||||
local stype sid svc
|
||||
|
||||
# Prepare container's /etc
|
||||
#
|
||||
# Since we also need the various test suite related dropins from the host's /etc,
|
||||
# we'll overlay our customizations on top of that
|
||||
mkdir -p "/var/lib/machines/$container/etc/systemd/dnssd"
|
||||
# Create 20 test services for each service type (_testServiceX._udp) and number them sequentially,
|
||||
# i.e. create services 0-19 for _testService0._udp, services 20-39 for _testService1._udp, and so on
|
||||
for stype in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
for sid in $(seq 0 $((SERVICE_COUNT - 1))); do
|
||||
svc=$((stype * SERVICE_COUNT + sid))
|
||||
|
||||
cat >"/var/lib/machines/$container/etc/systemd/dnssd/test-service-$container-$svc.dnssd" <<EOF
|
||||
[Service]
|
||||
Name=Test Service $svc on %H
|
||||
Type=_testService$stype._udp
|
||||
Port=98010
|
||||
TxtText=DC=Device PN=123456 SN=1234567890
|
||||
EOF
|
||||
done
|
||||
done
|
||||
|
||||
# To make things fast, spawn the container with a transient version of what's currently the host's
|
||||
# rootfs, with a couple of tweaks to make the container unique enough
|
||||
mkdir -p "/run/systemd/system/systemd-nspawn@$container.service.d"
|
||||
cat >"/run/systemd/system/systemd-nspawn@$container.service.d/override.conf" <<EOF
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
|
||||
--volatile=yes --directory=/ \
|
||||
--inaccessible=/etc/machine-id \
|
||||
--inaccessible=/etc/hostname \
|
||||
--resolv-conf=replace-stub \
|
||||
--network-zone=$CONTAINER_ZONE \
|
||||
--overlay=/etc:/var/lib/machines/$container/etc::/etc \
|
||||
--hostname=$container
|
||||
EOF
|
||||
}
|
||||
|
||||
check_both() {
|
||||
local service_id="${1:?}"
|
||||
local result_file="${2:?}"
|
||||
|
||||
# We should get 20 services per container, 40 total
|
||||
if [[ "$(wc -l <"$result_file")" -ge 40 ]]; then
|
||||
# Check if the services we got are the correct ones
|
||||
for i in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
svc=$((service_id * SERVICE_COUNT + i))
|
||||
if ! grep "Test Service $svc on $CONTAINER_1" "$result_file" ||
|
||||
! grep "Test Service $svc on $CONTAINER_2" "$result_file"; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
# We got all records and all of them are what we expect
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
check_first() {
|
||||
local service_id="${1:?}"
|
||||
local result_file="${2:?}"
|
||||
|
||||
# We should get 20 services per container
|
||||
if [[ "$(wc -l <"$result_file")" -ge 20 ]]; then
|
||||
# Check if the services we got are the correct ones
|
||||
for i in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
svc=$((service_id * SERVICE_COUNT + i))
|
||||
if ! grep "Test Service $svc on $CONTAINER_1" "$result_file"; then
|
||||
return 1
|
||||
fi
|
||||
# This check assumes the second container is unreachable, so this shouldn't happen
|
||||
if grep "Test Service $svc on $CONTAINER_2" "$result_file"; then
|
||||
echo >&2 "Found a record from an unreachable container"
|
||||
cat "$result_file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# We got all records and all of them are what we expect
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
run_and_check_services() {
|
||||
local service_id="${1:?}"
|
||||
local check_func="${2:?}"
|
||||
local unit_name="varlinkctl-$service_id-$SRANDOM.service"
|
||||
local i out_file parameters service_type svc tmp_file
|
||||
|
||||
out_file="$(mktemp)"
|
||||
error_file="$(mktemp)"
|
||||
tmp_file="$(mktemp)"
|
||||
service_type="_testService$service_id._udp"
|
||||
parameters="{ \"domain\": \"$service_type.local\", \"type\": \"\", \"ifindex\": ${BRIDGE_INDEX:?}, \"flags\": 16785432 }"
|
||||
|
||||
systemd-run --unit="$unit_name" --service-type=exec -p StandardOutput="file:$out_file" -p StandardError="file:$error_file" \
|
||||
varlinkctl call --more /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.BrowseServices "$parameters"
|
||||
|
||||
# shellcheck disable=SC2064
|
||||
# Note: unregister the trap once it's fired, otherwise it'll get propagated to functions that call this
|
||||
# one, *sigh*
|
||||
|
||||
trap "trap - RETURN; systemctl stop $unit_name" RETURN
|
||||
|
||||
for _ in {0..14}; do
|
||||
# The response format, for reference (it's JSON-SEQ):
|
||||
#
|
||||
# {
|
||||
# "browser_service_data": [
|
||||
# {
|
||||
# "updateFlag": true,
|
||||
# "family": 10,
|
||||
# "name": "Test Service 13 on test-mdns-1",
|
||||
# "type": "_testService0._udp",
|
||||
# "domain": "local",
|
||||
# "interface": 3
|
||||
# },
|
||||
# ...
|
||||
# ]
|
||||
# }
|
||||
if [[ -s "$out_file" ]]; then
|
||||
# Extract the service name from each valid record...
|
||||
# jq --slurp --raw-output \
|
||||
# ".[].browser_service_data[] | select(.updateFlag == true and .type == \"$service_type\" and .family == 10).name" "$out_file" | sort | tee "$tmp_file"
|
||||
grep -o '"name":"[^"]*"' "$out_file" | sed 's/"name":"//;s/"//g' | sort | tee "$tmp_file"
|
||||
# ...and compare them with what we expect
|
||||
if "$check_func" "$service_id" "$tmp_file"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
|
||||
cat "$out_file"
|
||||
cat "$error_file"
|
||||
return 1
|
||||
}
|
||||
|
||||
testcase_all_sequential() {
|
||||
: "Test each service type (sequentially)"
|
||||
resolvectl flush-caches
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_both
|
||||
done
|
||||
|
||||
echo testcase_end
|
||||
}
|
||||
|
||||
testcase_all_parallel() {
|
||||
: "Test each service type (in parallel)"
|
||||
resolvectl flush-caches
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_both &
|
||||
done
|
||||
wait
|
||||
}
|
||||
|
||||
testcase_single_service_multiple_times() {
|
||||
: "Test one service type multiple times"
|
||||
resolvectl flush-caches
|
||||
for _ in {0..4}; do
|
||||
run_and_check_services 4 check_both
|
||||
done
|
||||
}
|
||||
|
||||
testcase_second_unreachable() {
|
||||
: "Test each service type while the second container is unreachable"
|
||||
systemd-run -M "$CONTAINER_2" --wait --pipe -- networkctl down host0
|
||||
resolvectl flush-caches
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_first
|
||||
done
|
||||
|
||||
|
||||
: "Test each service type after bringing the second container back up again"
|
||||
systemd-run -M "$CONTAINER_2" --wait --pipe -- networkctl up host0
|
||||
systemd-run -M "$CONTAINER_2" --wait --pipe -- \
|
||||
/usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=host0 --operational-state=degraded --timeout=30
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_both
|
||||
done
|
||||
}
|
||||
|
||||
: "Setup host & containers"
|
||||
# Note: create the drop-in intentionally under /run/ and copy it manually into the containers
|
||||
mkdir -p /run/systemd/resolved.conf.d/
|
||||
cat >/run/systemd/resolved.conf.d/99-mdns-llmnr.conf <<EOF
|
||||
[Resolve]
|
||||
MulticastDNS=yes
|
||||
LLMNR=yes
|
||||
EOF
|
||||
|
||||
systemctl unmask systemd-resolved.service systemd-networkd.{service,socket} systemd-machined.service
|
||||
systemctl enable --now systemd-resolved.service systemd-networkd.{socket,service} systemd-machined.service
|
||||
systemctl reload systemd-resolved.service systemd-networkd.service
|
||||
|
||||
for container in "$CONTAINER_1" "$CONTAINER_2"; do
|
||||
create_container "$container"
|
||||
mkdir -p "/var/lib/machines/$container/etc/systemd/resolved.conf.d/"
|
||||
cp /run/systemd/resolved.conf.d/99-mdns-llmnr.conf "/var/lib/machines/$container/etc/systemd/resolved.conf.d/"
|
||||
touch "/var/lib/machines/$container/etc/hostname"
|
||||
systemctl daemon-reload
|
||||
machinectl start "$container"
|
||||
# Wait for the system bus to start...
|
||||
timeout 30s bash -xec "while ! systemd-run -M '$container' --wait --pipe true; do sleep 1; done"
|
||||
# ...and from there wait for the machine bootup to finish. We don't really care if the container
|
||||
# boots up in a degraded state, hence the `:`
|
||||
timeout 30s systemd-run -M "$container" --wait --pipe -- systemctl --wait is-system-running || :
|
||||
# Wait until the veth interface is configured and turn on mDNS and LLMNR
|
||||
systemd-run -M "$container" --wait --pipe -- \
|
||||
/usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=host0 --operational-state=degraded --timeout=30
|
||||
systemd-run -M "$container" --wait --pipe -- resolvectl mdns host0 yes
|
||||
systemd-run -M "$container" --wait --pipe -- resolvectl llmnr host0 yes
|
||||
systemd-run -M "$container" --wait --pipe -- networkctl status --no-pager
|
||||
systemd-run -M "$container" --wait --pipe -- resolvectl status --no-pager
|
||||
[[ "$(systemd-run -M "$container" --wait --pipe -- resolvectl mdns host0)" =~ :\ yes$ ]]
|
||||
[[ "$(systemd-run -M "$container" --wait --pipe -- resolvectl llmnr host0)" =~ :\ yes$ ]]
|
||||
done
|
||||
|
||||
BRIDGE_INDEX="$(<"/sys/class/net/vz-$CONTAINER_ZONE/ifindex")"
|
||||
machinectl list
|
||||
resolvectl mdns "vz-$CONTAINER_ZONE" on
|
||||
resolvectl llmnr "vz-$CONTAINER_ZONE" on
|
||||
networkctl status
|
||||
resolvectl status
|
||||
|
||||
# Run the actual test cases (functions prefixed by testcase_)
|
||||
run_testcases
|
||||
|
||||
touch /testok
|
||||
Reference in New Issue
Block a user