integration: speed up check for listening interfaces in system_setup

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-08-23 17:49:48 +02:00
parent f4efde9dba
commit d182b3bd69
2 changed files with 97 additions and 32 deletions

View File

@ -248,38 +248,10 @@ if [ "${RELEASE%.*}" -ge 20 ]; then
kill $subiquity_pid || true
exit 1
fi
loopback_failed=0
unallowed_failed=0
# Assert that only loopback interface is accepted.
interfaces=($(ip --json link show up | jq -r '.[]["ifname"] | select ( . != null )'))
for if in ${interfaces[@]}; do
for ipv in 4 6; do
curl_ec=0
timeout 10s \
curl -$ipv "http://localhost:$port/meta/status" --interface $if \
|| curl_ec=$?
# Loopback should exit 0 on IPv4
if [ $if = "lo" ]; then
if [ $curl_ec -ne 0 -a $ipv -eq 4 ]; then
loopback_failed=1
fi
# Everything else should not.
else
if [ $curl_ec -eq 0 ]; then
unallowed_failed=1
fi
fi
done
done
kill $subiquity_pid || true
if [ $loopback_failed -ne 0 ]; then
echo "Loopback was expected to connect"
exit 1
fi
if [ $unallowed_failed -ne 0 ]; then
echo "Only the loopback interface should be allowed."
exit 1
fi
scripts/test-system-setup-loopback-only.py --port "$port" --debug
kill -- "$subiquity_pid" || true
# Test system_setup autoinstall.
for mode in "" "-full" "-no-shutdown"; do

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python3
""" Makes sure system_setup is only listening to the loopback interface. """
import argparse
import asyncio
from dataclasses import dataclass
import json
import logging
import subprocess
from typing import List
class FailedTestCase(Exception):
pass
@dataclass
class Test:
interface: str
url: str
family: int
expect_success: bool
def read_network_interfaces() -> List[str]:
""" Return a list of network interfaces that are up. """
cmd = ["ip", "--json", "link", "show", "up"]
output = subprocess.check_output(cmd, text=True)
data = json.loads(output)
return [iface["ifname"] for iface in data if iface.get("ifname")]
async def test_connect(cmd: List[str]) -> bool:
""" Return true if the command specified exits with status 0 within 10
seconds. """
proc = await asyncio.create_subprocess_exec(
*cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
await asyncio.wait_for(proc.wait(), 10)
except asyncio.TimeoutError:
return False
return proc.returncode == 0
async def run_test(test: Test) -> None:
""" Execute a test and raise a FailedTestCase if it fails. """
logging.debug("Test: %s", test)
cmd = ["curl", f"-{test.family}", test.url, "--interface", test.interface]
status = await test_connect(cmd)
if status != test.expect_success:
logging.error("cmd %s exited %s but we expected %s", cmd,
"successfully" if status else "unsuccessfully",
"success" if test.expect_success else "failure")
raise FailedTestCase
async def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--debug", action="store_true")
parser.add_argument("--port", type=int, default=50321)
args = vars(parser.parse_args())
if args["debug"]:
logging.getLogger().level = logging.DEBUG
interfaces = read_network_interfaces()
logging.debug("interfaces = %s", interfaces)
coroutines = []
url = f"http://localhost:{args['port']}/meta/status"
for iface in interfaces:
for family in 4, 6:
if family == 4 and iface == "lo":
# Loopback should succeed on IPv4
expect_success=True
else:
# Everything else should not
expect_success = False
coroutines.append(run_test(Test(
interface=iface, url=url, family=family,
expect_success=expect_success)))
results = await asyncio.gather(*coroutines, return_exceptions=True)
if any(map(lambda x: isinstance(x, FailedTestCase), results)):
raise FailedTestCase
if __name__ == "__main__":
asyncio.run(main())