Add DNS hostname resolution ping tests (veth + tap+relay variants)
Two peers on the same intern network, one started with --hostname peer-a, resolve and ping each other by name via the per-session dnsmasq resolver (network_dns.cpp). Skips cleanly when dnsmasq isn't available. wait_for_hostname_then() retries the whole ping-by-name probe (not just an eth0 existence check) until it succeeds or the bound is hit, since this races against two independent things starting concurrently with the sandboxed command: the interface coming up, and the per-session resolver picking up the peer's own hosts record -- same underlying join_networks() timing limitation the earlier wait_for_eth0_then() fix was for. Verified end-to-end as root, both variants.
This commit is contained in:
@@ -45,6 +45,7 @@
|
|||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "config_file.h"
|
#include "config_file.h"
|
||||||
#include "fixtures.h"
|
#include "fixtures.h"
|
||||||
|
#include "network_dns.h"
|
||||||
#include "network_subnet.h"
|
#include "network_subnet.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -245,6 +246,23 @@ std::string wait_for_eth0_then(const std::string& command_after_eth0) {
|
|||||||
command_after_eth0);
|
command_after_eth0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Like wait_for_eth0_then(), but for a command that resolves `hostname`
|
||||||
|
// before using it: retries the whole ping-by-name probe (not just an eth0
|
||||||
|
// existence check) until it succeeds or the bound is hit, then runs the
|
||||||
|
// real, assertable command. A single probe covers two independent races
|
||||||
|
// against the same command starting concurrently with its own join
|
||||||
|
// (network_join.{h,cpp}'s own already-documented limitation): the
|
||||||
|
// interface itself not being up yet, and the per-session dnsmasq resolver
|
||||||
|
// (network_dns.cpp's start_dns_resolver(), started from the same
|
||||||
|
// on_bwrap_pid_known callback as the join itself) not having started, or
|
||||||
|
// not yet having picked up the peer's own hosts record, yet.
|
||||||
|
std::string wait_for_hostname_then(const std::string& hostname, const std::string& command_after) {
|
||||||
|
return fmt::format(
|
||||||
|
"echo BEGIN-TEST-OUTPUT; for i in $(seq 1 20); do ping -c 1 -W 1 {0} >/dev/null 2>&1 && break; "
|
||||||
|
"sleep 0.5; done; {1}; echo END-TEST-OUTPUT",
|
||||||
|
hostname, command_after);
|
||||||
|
}
|
||||||
|
|
||||||
// Runs `command` inside a fresh container joined to `networks`, in the
|
// Runs `command` inside a fresh container joined to `networks`, in the
|
||||||
// foreground, returning its captured stdout -- the same dispatch_command()
|
// foreground, returning its captured stdout -- the same dispatch_command()
|
||||||
// path BackgroundPeer's own forked child uses, just synchronous.
|
// path BackgroundPeer's own forked child uses, just synchronous.
|
||||||
@@ -519,3 +537,68 @@ TEST_CASE("network join: two intern peers can ping each other by IP (tap+relay)"
|
|||||||
}
|
}
|
||||||
CHECK(found_success);
|
CHECK(found_success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("network join: two peers can resolve and ping each other by hostname (veth)", "[integration][root][net]") {
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
SKIP("requires root");
|
||||||
|
}
|
||||||
|
if (!is_dnsmasq_available()) {
|
||||||
|
SKIP("dnsmasq not available");
|
||||||
|
}
|
||||||
|
auto image = find_busybox_fixture();
|
||||||
|
if (!image) {
|
||||||
|
SKIP("no busybox fixture (images/busybox.tar) -- see tests/setup-tests.py");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScratchXdgDirs scratch;
|
||||||
|
TestNetwork network("selftest-dns-ping-veth", NetworkKind::intern, /*veth=*/true);
|
||||||
|
REQUIRE(network.created());
|
||||||
|
|
||||||
|
BackgroundPeer peer_a(*image, {network.name()}, std::string("peer-a"), 20);
|
||||||
|
REQUIRE(peer_a.ready());
|
||||||
|
|
||||||
|
auto output = run_networked(
|
||||||
|
*image, {network.name()},
|
||||||
|
{"sh", "-c", wait_for_hostname_then("peer-a", "ping -c 2 -W 2 peer-a; echo RESULT=$?")});
|
||||||
|
auto lines = extract_marked_lines(output);
|
||||||
|
bool found_success = false;
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
if (line == "RESULT=0") {
|
||||||
|
found_success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(found_success);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("network join: two peers can resolve and ping each other by hostname (tap+relay)",
|
||||||
|
"[integration][root][net]") {
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
SKIP("requires root");
|
||||||
|
}
|
||||||
|
if (!is_dnsmasq_available()) {
|
||||||
|
SKIP("dnsmasq not available");
|
||||||
|
}
|
||||||
|
auto image = find_busybox_fixture();
|
||||||
|
if (!image) {
|
||||||
|
SKIP("no busybox fixture (images/busybox.tar) -- see tests/setup-tests.py");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScratchXdgDirs scratch;
|
||||||
|
TestNetwork network("selftest-dns-ping-tap", NetworkKind::intern, /*veth=*/false);
|
||||||
|
REQUIRE(network.created());
|
||||||
|
|
||||||
|
BackgroundPeer peer_a(*image, {network.name()}, std::string("peer-a"), 20);
|
||||||
|
REQUIRE(peer_a.ready());
|
||||||
|
|
||||||
|
auto output = run_networked(
|
||||||
|
*image, {network.name()},
|
||||||
|
{"sh", "-c", wait_for_hostname_then("peer-a", "ping -c 2 -W 2 peer-a; echo RESULT=$?")});
|
||||||
|
auto lines = extract_marked_lines(output);
|
||||||
|
bool found_success = false;
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
if (line == "RESULT=0") {
|
||||||
|
found_success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK(found_success);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user