Let a partner adopt the owner's custom networks from the conf phase 1 already cached, instead of asking for a value it cannot know

This commit is contained in:
Gmer4Lfe
2026-08-16 21:20:59 -04:00
parent 662f4f0d53
commit 3ae6298656
3 changed files with 111 additions and 2 deletions
+46 -2
View File
@@ -51,7 +51,7 @@ $detectedHostname = vv_get_hostname();
// Matching is exact and case-insensitive, never a prefix: two hosts called Tower and Tower2 must
// not resolve to each other, and there is no ambiguity to tolerate when the value was written by
// the very host it names.
$vvDetected = ['slot' => '', 'primary' => '', 'role' => ''];
$vvDetected = ['slot' => '', 'primary' => '', 'role' => '', 'networks' => [], 'networks_src' => ''];
$vvMasterRaw = vv_read_conf_raw('master.conf');
if ($vvMasterRaw !== '' && $detectedHostname !== '') {
preg_match_all('/^\s*(HOST\d+)\s*=\s*"([^"]*)"/m', $vvMasterRaw, $vvHm, PREG_SET_ORDER);
@@ -70,6 +70,34 @@ if ($vvMasterRaw !== '' && $detectedHostname !== '') {
// The primary is whatever HOST1 says, and it is only useful to a host that is not HOST1.
if ($vvDetected['role'] === 'partner') $vvDetected['primary'] = $vvSlots['host1'] ?? '';
}
// ── Custom docker networks, adopted from the owner ───────────────────────────────────────────
// master.conf names the hosts; it does not name the networks, which live in each host's own
// host*.conf. The owner's copy is available here anyway: onboard Phase 1 caches it into
// VV_CONF_RAM_CACHE_DIR as soon as SSH works, before this node has finished setup.
//
// This is the value a fresh mirror cannot know and cannot be expected to type. host.conf.template
// ships NETWORK_CONNECT_NETWORKS with its only entry commented out, so a mirror comes up with an
// empty list while the owner deploys containers onto a network named in the owner's templates —
// which is exactly how twelve containers ended up created against a network that did not exist.
//
// Adopting the owner's list also means docker_network_connect.sh will recreate the network here
// after an Unraid update wipes it, which is the whole reason that script exists.
if ($vvDetected['role'] === 'partner' && $vvDetected['slot'] !== '') {
$vvOwnerConf = VV_CONF_RAM_CACHE_DIR . '/host1.conf';
if (is_readable($vvOwnerConf)) {
$vvOwnerRaw = (string)@file_get_contents($vvOwnerConf);
$vvNets = vv_parse_conf_list($vvOwnerRaw, 'HOST1_NETWORK_CONNECT_NETWORKS');
// br* is ipvlan/macvlan tied to the owner's own hardware — its parent interface does not
// transfer, and adopting the name would attach this host's containers to the wrong thing.
$vvNets = array_values(array_filter($vvNets, fn($n) =>
$n !== '' && !preg_match('/^(bridge|host|none|br\d)/i', $n)));
if ($vvNets) {
$vvDetected['networks'] = $vvNets;
$vvDetected['networks_src'] = basename($vvOwnerConf);
}
}
}
?>
<link rel="stylesheet" href="/plugins/varaverk/css/varaverk.css">
<style>
@@ -333,6 +361,18 @@ function vvApplyDetectedIdentity() {
+ `${d.primary}. Change anything below if that is wrong.`
: 'Identity read from master.conf — this server is HOST1, the primary.';
banner.appendChild(note);
// Named separately from identity: this one is adopted from the owner's cached host conf,
// not from master.conf, and it is the value the operator would otherwise have to know.
if (Array.isArray(d.networks) && d.networks.length) {
const nets = document.createElement('div');
nets.style.cssText = 'margin-top:6px;padding:7px 10px;border-radius:3px;background:#0d1a28;'
+ 'border:1px solid #1a3a5a;color:#7ab;font-size:11px;line-height:1.5;';
nets.textContent = `Custom network${d.networks.length > 1 ? 's' : ''} adopted from `
+ `${d.primary || 'the primary'}: ${d.networks.join(', ')} — added to this `
+ `server's conf on save, so the containers it deploys here have somewhere to land.`;
banner.appendChild(nets);
}
}
}
@@ -549,9 +589,13 @@ function vvDoSave() {
if (mySlot === 'host2') host2 = hostname;
}
vvSetBtn('Saving…', true);
// Networks come from the owner's cached conf, not from any field — there is nothing for the
// operator to type and nothing to get wrong. Empty on the primary, and on a partner whose
// owner conf has not been cached yet; the save handler treats absent as "nothing to adopt".
const networks = (vvDetectedIdentity?.networks ?? []).join(',');
fetch('/plugins/varaverk/api/setup.php', {
method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({action:'save', host1, host2, my_slot:mySlot, my_hostname:hostname, storage_mode:_vvStorageMode})
body: new URLSearchParams({action:'save', host1, host2, my_slot:mySlot, my_hostname:hostname, storage_mode:_vvStorageMode, networks})
}).then(r => r.json()).then(d => {
if (d.ok) {
if (d.needs_migration) {