diff --git a/Plugin/unraid/api/fallback_coverage.php b/Plugin/unraid/api/fallback_coverage.php index 76de81d..02ac2be 100644 --- a/Plugin/unraid/api/fallback_coverage.php +++ b/Plugin/unraid/api/fallback_coverage.php @@ -88,6 +88,30 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') { } sort($containers, SORT_NATURAL | SORT_FLAG_CASE); + // Containers the partner already runs continuously, because onboard deployed them there. + // They cannot be failover material: there is nothing to start during an outage that is not + // already up, and a tier assignment for one would be a delay applied to a running container. + // + // The stacks store XML template filenames, and their case does not have to agree with the + // container's — this conf ships "my-prowlarr.xml" against a container named "Prowlarr" — so + // the mapping is done case-insensitively, once, here. + $stackOf = []; + $byLower = []; + foreach ($containers as $n) $byLower[strtolower($n)] = $n; + foreach ([ + 'auth' => "{$hostUp}_PARTNERSHIP_AUTH_STACK", + 'arrs' => "{$hostUp}_PARTNERSHIP_ARR_STACK", + 'services' => "{$hostUp}_PARTNERSHIP_SERVICES_STACK", + ] as $label => $var) { + foreach (vv_parse_conf_list($raw, $var) as $xml) { + $n = preg_replace('/^my-|\.xml$/', '', trim($xml)); + if ($n === '') continue; + // The container's real name where this host has one, the template's otherwise: a + // stack entry for something not installed here is still a stack entry. + $stackOf[$byLower[strtolower($n)] ?? $n] = $label; + } + } + // Named in a tier but not installed here. Reported rather than filtered: a tier entry for a // container that does not exist is a line fallback.sh fails on during an outage, which is // the worst possible moment to find a typo. @@ -100,6 +124,7 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') { 'host' => $hostUp, 'containers' => $containers, 'cover' => (object)$cover, + 'stacks' => (object)$stackOf, 'missing' => $missing, 'tier_vars' => array_map($tierVar, $TIERS), ]); @@ -125,6 +150,22 @@ foreach ($TIERS as $t) { } } + +// Stack containers refused here, not only greyed out in the picker. The card disables their +// selects, but a disabled input is a courtesy to the operator, not a constraint on the endpoint — +// and a tier assignment for a container the partner already runs permanently is a delay attached +// to something that never stops. +$stackNames = []; +foreach ([ + "{$hostUp}_PARTNERSHIP_AUTH_STACK", + "{$hostUp}_PARTNERSHIP_ARR_STACK", + "{$hostUp}_PARTNERSHIP_SERVICES_STACK", +] as $var) { + foreach (vv_parse_conf_list($existingRaw, $var) as $xml) { + $n = preg_replace('/^my-|\.xml$/', '', trim($xml)); + if ($n !== '') $stackNames[strtolower($n)] = true; + } +} $map = json_decode((string)($_POST['tiers'] ?? ''), true); if (!is_array($map)) { echo json_encode(['ok' => false, 'error' => 'tiers must be an object']); exit; } @@ -137,6 +178,10 @@ foreach ($map as $name => $tier) { if (!isset($known[strtolower((string)$name)])) { echo json_encode(['ok' => false, 'error' => "No container named $name on this host"]); exit; } + if (isset($stackNames[strtolower((string)$name)])) { + echo json_encode(['ok' => false, + 'error' => "$name is deployed to the partner as part of a stack — it already runs there, so it cannot be given a fallback tier"]); exit; + } $byTier[$t][] = $known[strtolower((string)$name)]; } diff --git a/Plugin/unraid/pages/fallback.php b/Plugin/unraid/pages/fallback.php index 769d0f2..29a6d12 100644 --- a/Plugin/unraid/pages/fallback.php +++ b/Plugin/unraid/pages/fallback.php @@ -55,6 +55,46 @@ require_once dirname(__DIR__) . '/include/ai_chat.php'; if (vv_ai_ui_on()) vv_ai_chat_assets(); ?>