Codex-Runde 2: der Gateway-Fix erreichte den Produktivpfad nicht
Zwei Befunde, beide echt, und der erste ist ein Patzer:
detect_network_style wurde auf default_gateway() umgestellt,
bridge-run.sh blieb auf awk '{ print }'. Der Treiber reichte also
weiter den KARTENNAMEN als Gateway an build_bridge, und in der Strophe
stand 'gateway ens3' — der Fix half genau der Stelle nicht, fuer die er
gedacht war.
Meine Sandkiste konnte das nicht sehen: sie hatte immer ein via. Jetzt
ist sie parametrisiert, und ein Test faehrt bridge-run.sh mit einer
via-losen Standardroute durch und liest die geschriebene Strophe. Der
Beweis laeuft ueber den Produktivpfad, nicht ueber eine Einzelfunktion.
Zweitens: default_gateway suchte das erste via IRGENDWO in der Ausgabe,
detect_primary_interface das dev der ERSTEN Zeile. Bei zwei
Standardrouten baute das eine Bruecke ueber die Karte der einen mit dem
Gateway der anderen. Beide haengen jetzt an primary_default_route mit
head -1 — eine Route, eine Quelle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
main
parent
0bb61d39bd
commit
133b2cf552
|
|
@ -108,7 +108,10 @@ interface_is_physical "$_iface" \
|
|||
|
||||
_style="$(detect_network_style "$_iface")"
|
||||
_cidr="$("$CLUPILOT_IP" -4 -o addr show dev "$_iface" scope global 2>/dev/null | awk '{ print $4; exit }')"
|
||||
_gw="$("$CLUPILOT_IP" -4 route show default 2>/dev/null | awk '{ print $3; exit }')"
|
||||
# Über den Helfer, nicht mit `awk '{ print $3 }'`: bei `default dev ens3 scope
|
||||
# link` steht dort der KARTENNAME, und der landete als `gateway ens3` in der
|
||||
# Strophe. Beides — Karte und Gateway — kommt aus derselben Route.
|
||||
_gw="$(default_gateway)"
|
||||
|
||||
[ "$_style" = 'dhcp' ] || [ -n "$_cidr" ] \
|
||||
|| fail "Keine globale IPv4-Adresse auf ${_iface} — nichts, was auf eine Bruecke ziehen koennte."
|
||||
|
|
|
|||
|
|
@ -82,19 +82,28 @@ fi
|
|||
# Die Schnittstelle, über die die Vorgabe-Route geht. Nicht „die erste, die
|
||||
# nicht lo heißt": eine Maschine mit zwei Karten hat oft eine angeschlossene und
|
||||
# eine nicht.
|
||||
# Die EINE Standardroute, aus der alles andere abgeleitet wird.
|
||||
#
|
||||
# `head -1`, und alle drei Leser hängen daran. Ein Host kann mehrere haben, und
|
||||
# wer Karte und Gateway aus verschiedenen Zeilen zieht, baut eine Brücke über die
|
||||
# Karte der einen mit dem Gateway der anderen — ein Netz, das nirgends hinführt.
|
||||
primary_default_route() {
|
||||
"$CLUPILOT_IP" -4 route show default 2>/dev/null | head -1
|
||||
}
|
||||
|
||||
detect_primary_interface() {
|
||||
"$CLUPILOT_IP" -4 route show default 2>/dev/null \
|
||||
primary_default_route \
|
||||
| awk '{ for (i = 1; i < NF; i++) if ($i == "dev") { print $(i+1); exit } }'
|
||||
}
|
||||
|
||||
# Das Gateway der Standardroute — hinter dem `via`, nie an fester Feldnummer.
|
||||
# Das Gateway dieser Route — hinter dem `via`, nie an fester Feldnummer.
|
||||
#
|
||||
# `default dev ens3 scope link` hat keins. Ein `awk '{ print $3 }'` lieferte dort
|
||||
# den KARTENNAMEN, und daraus würde `gateway ens3` in der Strophe: `ifreload`
|
||||
# scheitert, der Zeitgeber räumt ab — und abgeräumt wird ein Host, der zu bauen
|
||||
# gewesen wäre. Leer heißt hier ehrlich leer.
|
||||
default_gateway() {
|
||||
"$CLUPILOT_IP" -4 route show default 2>/dev/null \
|
||||
primary_default_route \
|
||||
| awk '{ for (i = 1; i < NF; i++) if ($i == "via") { print $(i+1); exit } }'
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +111,7 @@ default_gateway() {
|
|||
# die muss die Brücke als eigene `up`-Zeile mitnehmen, sonst verliert der Host
|
||||
# seinen Weg nach draußen.
|
||||
has_default_route() {
|
||||
"$CLUPILOT_IP" -4 route show default 2>/dev/null | grep -q .
|
||||
primary_default_route | grep -q .
|
||||
}
|
||||
|
||||
# Ist das eine physische Karte?
|
||||
|
|
|
|||
|
|
@ -939,7 +939,7 @@ it('entmachtet cloud-init im gesicherten Stand', function () {
|
|||
*
|
||||
* @return array{work:string, calls:string}
|
||||
*/
|
||||
function bridgeSandbox(string $dir, bool $bridgeComesUp = true, int $handshakeAge = 5): array
|
||||
function bridgeSandbox(string $dir, bool $bridgeComesUp = true, int $handshakeAge = 5, bool $withGateway = true): array
|
||||
{
|
||||
$work = "{$dir}/work";
|
||||
$bin = "{$dir}/bin";
|
||||
|
|
@ -952,12 +952,15 @@ function bridgeSandbox(string $dir, bool $bridgeComesUp = true, int $handshakeAg
|
|||
file_put_contents("{$dir}/interfaces", "# die Fassung von vorher\n");
|
||||
|
||||
$up = $bridgeComesUp ? "[ -f '{$state}/bridged' ]" : 'false';
|
||||
// Eine Standardroute ohne `via` ist eine Link-Route. Sie hat kein Gateway,
|
||||
// und ein `awk '{ print $3 }'` läse dort den KARTENNAMEN.
|
||||
$route = $withGateway ? 'default via 10.0.0.1 dev $DEV' : 'default dev $DEV scope link';
|
||||
|
||||
file_put_contents("{$bin}/ip", <<<SH
|
||||
#!/bin/sh
|
||||
if {$up}; then DEV=vmbr0; else DEV=enp0s31f6; fi
|
||||
case "\$*" in
|
||||
'-4 route show default') echo "default via 10.0.0.1 dev \$DEV" ;;
|
||||
'-4 route show default') echo "{$route}" ;;
|
||||
'-4 -o addr show dev enp0s31f6 scope global')
|
||||
echo '2: enp0s31f6 inet 10.0.0.7/24 scope global enp0s31f6' ;;
|
||||
'-4 -o addr show dev vmbr0 scope global')
|
||||
|
|
@ -1028,6 +1031,40 @@ it('fährt den ganzen Ablauf durch und meldet ok', function () {
|
|||
->and(file_get_contents($box['calls']))->not->toContain('stop clupilot-network-rollback.timer');
|
||||
});
|
||||
|
||||
it('schreibt kein Gateway in die Strophe, wenn die Standardroute keins hat', function () {
|
||||
// Der Beweis über den PRODUKTIVPFAD, nicht über eine Einzelfunktion.
|
||||
// `detect_network_style` auf `default_gateway()` umzustellen half nichts,
|
||||
// solange bridge-run.sh sein Gateway weiter mit `awk '{ print $3 }'` las:
|
||||
// der Treiber reichte `ens3` an build_bridge weiter, und in der Strophe
|
||||
// stand `gateway ens3`. Ein Test an der Bibliothek allein hätte das nie
|
||||
// gesehen.
|
||||
$box = bridgeSandbox($this->dir, withGateway: false);
|
||||
|
||||
Process::path($box['work'])
|
||||
->env(['CLUPILOT_WORK_DIR' => $box['work']])
|
||||
->run('sh '.base_path('deploy/bootstrap/lib/bridge-run.sh'));
|
||||
|
||||
$stanza = (string) file_get_contents($this->dir.'/interfaces');
|
||||
|
||||
expect($stanza)->toContain('address 10.0.0.7/24')
|
||||
->not->toContain('gateway enp0s31f6')
|
||||
->not->toContain('pointopoint')
|
||||
// Die Link-Route geht sonst beim Umbau verloren.
|
||||
->toContain('up ip route add default dev vmbr0');
|
||||
});
|
||||
|
||||
it('nimmt Karte und Gateway aus DERSELBEN Standardroute', function () {
|
||||
// Bei zwei Standardrouten nahm `detect_primary_interface` das `dev` der
|
||||
// ersten und `default_gateway` das `via` der zweiten — die Brücke bekäme
|
||||
// die Karte der einen mit dem Gateway der anderen.
|
||||
$ip = fakeIp($this->dir, [
|
||||
'-4 route show default' => "default dev ens3 scope link\ndefault via 10.0.0.1 dev ens4 metric 200",
|
||||
]);
|
||||
|
||||
expect(runBridgeSh('detect_primary_interface', ['CLUPILOT_IP' => $ip]))->toBe('ens3')
|
||||
->and(runBridgeSh('default_gateway', ['CLUPILOT_IP' => $ip]))->toBe('');
|
||||
});
|
||||
|
||||
it('meldet failed und lässt den Zeitgeber stehen, wenn die Brücke nicht trägt', function () {
|
||||
// Der Fall, für den die ganze Konstruktion gebaut ist. Der Treiber räumt
|
||||
// NICHT selbst auf — er überlässt es dem Zeitgeber und sagt nur, warum.
|
||||
|
|
|
|||
Loading…
Reference in New Issue