Skip the key step when SSH already works, and read the install path live on both sides
This commit is contained in:
@@ -315,14 +315,33 @@ if [[ "$AM_MIRROR" == true ]]; then
|
|||||||
echo " Mirror sets up SSH keys, then notifies Owner to run Phase 2."
|
echo " Mirror sets up SSH keys, then notifies Owner to run Phase 2."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Tested before attempted, the same guard the owner's phase 1 has had all along.
|
||||||
|
#
|
||||||
|
# The key step is a TERMINAL step by design — ssh_setup.sh runs ssh-copy-id, which prompts for
|
||||||
|
# the owner's root password on a first install. Once the operator has done that in a terminal,
|
||||||
|
# pressing ▶ Onboard ran the whole thing again: another ssh-copy-id, this time from the WebGUI
|
||||||
|
# with no TTY to answer the prompt, which fails and aborts the run at Step 1 — so the button
|
||||||
|
# whose entire job is Step 2 could never reach it.
|
||||||
|
#
|
||||||
|
# Working SSH is the actual precondition, not "have we run the setup script". If it already
|
||||||
|
# works there is nothing to install, whichever route installed it.
|
||||||
|
OWNER_IP_PRE=$(resolve_tailscale_ip "$OWNER" 2>/dev/null || true)
|
||||||
if [[ "$SKIP_SSH" == true ]]; then
|
if [[ "$SKIP_SSH" == true ]]; then
|
||||||
warn "Skipping SSH setup (--skip-ssh)"
|
warn "Skipping SSH setup (--skip-ssh)"
|
||||||
|
elif [[ -n "$OWNER_IP_PRE" ]] && timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
|
||||||
|
-o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes -o StrictHostKeyChecking=no \
|
||||||
|
root@"$OWNER_IP_PRE" exit 0 2>/dev/null; then
|
||||||
|
echo "SSH to $OWNER already works ✅ — key already installed, skipping setup"
|
||||||
elif bash "$SCRIPT_DIR/ssh_setup.sh" "${EXTRA_FLAGS[@]}"; then
|
elif bash "$SCRIPT_DIR/ssh_setup.sh" "${EXTRA_FLAGS[@]}"; then
|
||||||
echo "SSH key ready ✅"
|
echo "SSH key ready ✅"
|
||||||
else
|
else
|
||||||
error "SSH key setup failed"
|
error "SSH key setup failed"
|
||||||
|
error "Install the key from a terminal on this host — ssh-copy-id needs $OWNER's password,"
|
||||||
|
error "and a WebGUI button has no way to answer that prompt:"
|
||||||
|
error " bash $SCRIPTS_ROOT/Partnership/partnership_onboard.sh --phase1-only"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
unset OWNER_IP_PRE
|
||||||
|
|
||||||
# Stop after the key when asked. ssh_setup.sh runs ssh-copy-id, which prompts for the
|
# Stop after the key when asked. ssh_setup.sh runs ssh-copy-id, which prompts for the
|
||||||
# owner's root password on a first install — answerable in a terminal, never from the
|
# owner's root password on a first install — answerable in a terminal, never from the
|
||||||
|
|||||||
@@ -203,6 +203,24 @@ function vvPrompt(text, def, opts) {
|
|||||||
//
|
//
|
||||||
// Not in js/varaverk.js: that loads below the tab include, and the wizard returns before it.
|
// Not in js/varaverk.js: that loads below the tab include, and the wizard returns before it.
|
||||||
|
|
||||||
|
// Where Varaverk is installed on THIS host, read live rather than baked into the page.
|
||||||
|
//
|
||||||
|
// Two reasons it cannot be a render-time constant. The wizard can move it: choosing appdata in
|
||||||
|
// step 1 triggers a migration and step 2 renders in the same page load, so PHP's value names the
|
||||||
|
// pre-migration location. And each host chooses independently — the owner may be on flash while
|
||||||
|
// the mirror is on appdata — so a panel rendered on either side must ask, not assume.
|
||||||
|
//
|
||||||
|
// It matters because the value ends up in a command the operator pastes into a root terminal.
|
||||||
|
// api/setup.php?action=detect re-reads varaverk.cfg. Cached; the panels re-render often.
|
||||||
|
let _vvScriptsDir = null;
|
||||||
|
function vvScriptsDir(fallback) {
|
||||||
|
if (_vvScriptsDir) return Promise.resolve(_vvScriptsDir);
|
||||||
|
return fetch('/plugins/varaverk/api/setup.php?action=detect&_=' + Date.now())
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => (_vvScriptsDir = (d && d.scripts_dir) || fallback))
|
||||||
|
.catch(() => (_vvScriptsDir = fallback));
|
||||||
|
}
|
||||||
|
|
||||||
// api/run.php answers when a job is LAUNCHED, not finished. An empty body is never success —
|
// api/run.php answers when a job is LAUNCHED, not finished. An empty body is never success —
|
||||||
// Unraid's CSRF guard exits with one, and so does a PHP fatal.
|
// Unraid's CSRF guard exits with one, and so does a PHP fatal.
|
||||||
function _vvPtRun(id, extraArgs) {
|
function _vvPtRun(id, extraArgs) {
|
||||||
|
|||||||
@@ -626,6 +626,11 @@ async function vvPtSaveSettings(btn) {
|
|||||||
// ── Private page logic ─────────────────────────────────────────────────────────
|
// ── Private page logic ─────────────────────────────────────────────────────────
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
// Resolve the install path once, up front, and redraw when it lands. termCmd is built
|
||||||
|
// synchronously inside _renderActions, so without this the first paint would use the
|
||||||
|
// render-time fallback — right on a flash host, wrong on one that moved to appdata.
|
||||||
|
vvScriptsDir(<?= json_encode(SCRIPTS_DIR) ?>).then(() => { if (_vvPtReload) _vvPtReload(); });
|
||||||
|
|
||||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function _row(lbl, val) {
|
function _row(lbl, val) {
|
||||||
@@ -950,7 +955,11 @@ function _renderActions(nodes, cfg) {
|
|||||||
const remotes = nodes.filter(n => !n.is_me);
|
const remotes = nodes.filter(n => !n.is_me);
|
||||||
const hasPartner = remotes.some(n => n.hostname);
|
const hasPartner = remotes.some(n => n.hostname);
|
||||||
const termBase = `https://${window.location.hostname}/webterminal/ttyd/`;
|
const termBase = `https://${window.location.hostname}/webterminal/ttyd/`;
|
||||||
const termCmd = `bash <?= SCRIPTS_DIR ?>/Partnership/partnership_onboard.sh --phase1-only`;
|
// _vvScriptsDir is filled by the live probe below and falls back to the render-time value.
|
||||||
|
// Both matter: a host in appdata must be handed its appdata path, and this panel is rendered
|
||||||
|
// on whichever side is the mirror — so it cannot assume either layout. The owner and the
|
||||||
|
// mirror are independently either flash or appdata.
|
||||||
|
const termCmd = `bash ${_vvScriptsDir || <?= json_encode(SCRIPTS_DIR) ?>}/Partnership/partnership_onboard.sh --phase1-only`;
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ if ($vvMasterRaw !== '' && $detectedHostname !== '') {
|
|||||||
if ($vvDetected['role'] === 'partner') $vvDetected['primary'] = $vvSlots['host1'] ?? '';
|
if ($vvDetected['role'] === 'partner') $vvDetected['primary'] = $vvSlots['host1'] ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── How far this host's own onboarding has got ───────────────────────────────────────────────
|
||||||
|
// Lets the inline join panel show the SSH step or the join button rather than both. Same flags
|
||||||
|
// api/checklist.php reads, and the same two spellings, because the state file has been written
|
||||||
|
// by both bash and PHP over its life.
|
||||||
|
if ($vvDetected['slot'] !== '') {
|
||||||
|
$vvSt = vv_setup_state_read();
|
||||||
|
$vvSlUp = strtoupper($vvDetected['slot']);
|
||||||
|
$vvDetected['phase'] =
|
||||||
|
(!empty($vvSt[$vvSlUp . '_PHASE2_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase2_done'])) ? 2
|
||||||
|
: ((!empty($vvSt[$vvSlUp . '_PHASE1_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase1_done'])) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Custom docker networks, adopted from the owner ───────────────────────────────────────────
|
// ── 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
|
// 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
|
// host*.conf. The owner's copy is available here anyway: onboard Phase 1 caches it into
|
||||||
@@ -84,17 +96,6 @@ if ($vvMasterRaw !== '' && $detectedHostname !== '') {
|
|||||||
//
|
//
|
||||||
// Adopting the owner's list also means docker_network_connect.sh will recreate the network here
|
// 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.
|
// after an Unraid update wipes it, which is the whole reason that script exists.
|
||||||
// How far this host's own onboarding has got, so the inline join panel can show the SSH step or
|
|
||||||
// the join button rather than both. Same flags api/checklist.php reads, and the same two
|
|
||||||
// spellings, because the state file has been written by both bash and PHP over its life.
|
|
||||||
if ($vvDetected['slot'] !== '') {
|
|
||||||
$vvSt = vv_setup_state_read();
|
|
||||||
$vvSlUp = strtoupper($vvDetected['slot']);
|
|
||||||
$vvDetected['phase'] =
|
|
||||||
(!empty($vvSt[$vvSlUp . '_PHASE2_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase2_done'])) ? 2
|
|
||||||
: ((!empty($vvSt[$vvSlUp . '_PHASE1_DONE']) || !empty($vvSt[$vvDetected['slot'] . '_phase1_done'])) ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($vvDetected['role'] === 'partner' && $vvDetected['slot'] !== '') {
|
if ($vvDetected['role'] === 'partner' && $vvDetected['slot'] !== '') {
|
||||||
$vvOwnerConf = VV_CONF_RAM_CACHE_DIR . '/host1.conf';
|
$vvOwnerConf = VV_CONF_RAM_CACHE_DIR . '/host1.conf';
|
||||||
if (is_readable($vvOwnerConf)) {
|
if (is_readable($vvOwnerConf)) {
|
||||||
@@ -506,26 +507,6 @@ function vvDeferItem(btn, id, defer) {
|
|||||||
//
|
//
|
||||||
// Only while it is still outstanding: once the mirror has joined, the partnership row in the
|
// Only while it is still outstanding: once the mirror has joined, the partnership row in the
|
||||||
// checklist above says so and a second copy of the panel is noise.
|
// checklist above says so and a second copy of the panel is noise.
|
||||||
// SCRIPTS_DIR is read live rather than baked into the page, because the wizard can MOVE it.
|
|
||||||
//
|
|
||||||
// Choosing appdata storage in step 1 triggers a migration, and step 2 renders in the same page
|
|
||||||
// load — so anything PHP substituted at render time still names the pre-migration location. A
|
|
||||||
// host that migrated was handed "bash /boot/config/plugins/varaverk/…" to paste, which does not
|
|
||||||
// exist there. The command is the one thing on this panel that must be right: it is pasted into
|
|
||||||
// a root terminal.
|
|
||||||
//
|
|
||||||
// api/setup.php?action=detect re-reads varaverk.cfg, so it reports wherever the install actually
|
|
||||||
// ended up. Cached after the first call — the checklist re-renders on every action.
|
|
||||||
let _vvScriptsDir = null;
|
|
||||||
function vvScriptsDir() {
|
|
||||||
if (_vvScriptsDir) return Promise.resolve(_vvScriptsDir);
|
|
||||||
return fetch('/plugins/varaverk/api/setup.php?action=detect&_=' + Date.now())
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(d => (_vvScriptsDir = (d && d.scripts_dir) || <?= json_encode(SCRIPTS_DIR) ?>))
|
|
||||||
// Falling back to the render-time value is still better than no command at all; it is only
|
|
||||||
// wrong in the case this function exists to handle, and the panel shows the path either way.
|
|
||||||
.catch(() => (_vvScriptsDir = <?= json_encode(SCRIPTS_DIR) ?>));
|
|
||||||
}
|
|
||||||
|
|
||||||
function vvRenderOnboardPanel(d) {
|
function vvRenderOnboardPanel(d) {
|
||||||
const el = document.getElementById('vv-onboard-panel');
|
const el = document.getElementById('vv-onboard-panel');
|
||||||
@@ -534,7 +515,7 @@ function vvRenderOnboardPanel(d) {
|
|||||||
const pt = (d.items || []).find(i => i.id === 'partnership');
|
const pt = (d.items || []).find(i => i.id === 'partnership');
|
||||||
if (!partner || !pt || pt.ok) { el.innerHTML = ''; return; }
|
if (!partner || !pt || pt.ok) { el.innerHTML = ''; return; }
|
||||||
|
|
||||||
vvScriptsDir().then(dir => {
|
vvScriptsDir(<?= json_encode(SCRIPTS_DIR) ?>).then(dir => {
|
||||||
el.innerHTML = '<hr class="vv-hr">'
|
el.innerHTML = '<hr class="vv-hr">'
|
||||||
+ '<div class="vv-cl-title">Join the partnership</div>'
|
+ '<div class="vv-cl-title">Join the partnership</div>'
|
||||||
+ vvRenderMirrorOnboard({
|
+ vvRenderMirrorOnboard({
|
||||||
|
|||||||
Reference in New Issue
Block a user