Phase 1: resilient SSH handling + Push Conf button
partnership_onboard.sh (--phase1-only): - Test if SSH already works first (BatchMode) — skip ssh-copy-id if yes - If ssh-copy-id fails (no TTY in background): soft-fail, generate key locally, print pubkey for manual install, write HOST2_KEY_READY=true, run local setup - Only skip conf push if SSH truly unavailable; otherwise proceeds normally - Exit 0 with clear manual-step instructions instead of hard failure include/partnership.php: - Read HOST2_KEY_READY from setup.db; expose as key_ready on remote nodes pages/partnership.php: - Node card: amber "Key generated · install on HOST2 then Push Conf" badge when key_ready=true and phase=0 - "Push Conf (key installed)" button appears when key_ready=true — runs --phase1-only --skip-ssh (assumes user manually did ssh-copy-id) - vvPtPushConf() top-level function
This commit is contained in:
@@ -559,8 +559,47 @@ MASTER_PUSH_OK=false
|
|||||||
echo "━━━ Step 1 — SSH Key Setup ━━━"
|
echo "━━━ Step 1 — SSH Key Setup ━━━"
|
||||||
|
|
||||||
if [[ "$SKIP_SSH" == true ]]; then
|
if [[ "$SKIP_SSH" == true ]]; then
|
||||||
warn "Skipping (--skip-ssh / --phase2-only)"
|
warn "Skipping (--skip-ssh)"
|
||||||
STEP_SSH_OK=true
|
STEP_SSH_OK=true
|
||||||
|
elif [[ "$PHASE1_ONLY" == true ]]; then
|
||||||
|
# Phase 1 in background: test if SSH already works first — avoids ssh-copy-id
|
||||||
|
# hanging for a password prompt with no TTY.
|
||||||
|
if timeout "$SSH_TIMEOUT" ssh -i "$SSH_KEY" \
|
||||||
|
-o ConnectTimeout="$SSH_TIMEOUT" -o BatchMode=yes root@"$MIRROR_IP" exit 0 2>/dev/null; then
|
||||||
|
log "SSH to $MIRROR already works ✅ — skipping key install"
|
||||||
|
STEP_SSH_OK=true
|
||||||
|
else
|
||||||
|
# Key not yet on HOST2 — try ssh_setup.sh (works interactively, may fail in background)
|
||||||
|
if bash "$SCRIPT_DIR/ssh_setup.sh" "${EXTRA_FLAGS[@]}"; then
|
||||||
|
log "SSH keys ready ✅"
|
||||||
|
STEP_SSH_OK=true
|
||||||
|
else
|
||||||
|
# Soft-fail: generate key locally if not present, then tell user to install manually
|
||||||
|
warn "Could not install key on $MIRROR automatically (no terminal for password prompt)"
|
||||||
|
if [[ -f "$SSH_KEY" ]]; then
|
||||||
|
log "Local key exists at: $SSH_KEY"
|
||||||
|
else
|
||||||
|
bash "$SCRIPT_DIR/ssh_setup.sh" --key-only "${EXTRA_FLAGS[@]}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [[ -f "${SSH_KEY}.pub" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo " Install this key on $MIRROR to complete SSH setup:"
|
||||||
|
echo " ┌─────────────────────────────────────────────────────"
|
||||||
|
cat "${SSH_KEY}.pub" | sed 's/^/ │ /'
|
||||||
|
echo " └─────────────────────────────────────────────────────"
|
||||||
|
echo " Run on a terminal: ssh-copy-id -i ${SSH_KEY}.pub root@${MIRROR_IP}"
|
||||||
|
echo " Then click 'Push Conf' in the Partnership tab."
|
||||||
|
# Write key-ready flag so UI can show the manual-install state
|
||||||
|
[[ "$DRY_RUN" == false ]] && {
|
||||||
|
local kflag="${MIRROR_ID}_KEY_READY"
|
||||||
|
grep -q "^${kflag}=" /boot/config/varaverk_setup.db 2>/dev/null \
|
||||||
|
&& sed -i "s|^${kflag}=.*|${kflag}=true|" /boot/config/varaverk_setup.db \
|
||||||
|
|| echo "${kflag}=true" >> /boot/config/varaverk_setup.db
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
STEP_SSH_OK=false
|
||||||
|
fi
|
||||||
|
fi
|
||||||
elif bash "$SCRIPT_DIR/ssh_setup.sh" "${EXTRA_FLAGS[@]}"; then
|
elif bash "$SCRIPT_DIR/ssh_setup.sh" "${EXTRA_FLAGS[@]}"; then
|
||||||
log "SSH keys ready ✅"
|
log "SSH keys ready ✅"
|
||||||
STEP_SSH_OK=true
|
STEP_SSH_OK=true
|
||||||
@@ -574,6 +613,30 @@ fi
|
|||||||
# --phase1-only: SSH + conf push is all HOST1 needs to do before HOST2 installs Varaverk.
|
# --phase1-only: SSH + conf push is all HOST1 needs to do before HOST2 installs Varaverk.
|
||||||
# HOST2's wizard will detect the pushed master.conf + state file and take the correct path.
|
# HOST2's wizard will detect the pushed master.conf + state file and take the correct path.
|
||||||
if [[ "$PHASE1_ONLY" == true ]]; then
|
if [[ "$PHASE1_ONLY" == true ]]; then
|
||||||
|
if [[ "$STEP_SSH_OK" == false ]]; then
|
||||||
|
# SSH key not yet installed on HOST2 — can't push conf, but local setup still runs.
|
||||||
|
# UI will show "key ready, install manually" state via HOST2_KEY_READY flag.
|
||||||
|
echo ""
|
||||||
|
echo "━━━ Phase 1 — HOST1 Local Setup (SSH pending) ━━━"
|
||||||
|
bash "$SCRIPT_DIR/partnership_manager.sh" --onboard --local-only "${EXTRA_FLAGS[@]}" || \
|
||||||
|
warn "Local setup had issues — FolderView3 may need manual setup"
|
||||||
|
|
||||||
|
END=$(date +%s)
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━ $ICON_SUMMARY PHASE 1 — SSH PENDING ━━━━━"
|
||||||
|
echo " SSH keys: key generated ✅ — NOT yet installed on $MIRROR ⚠"
|
||||||
|
echo " Conf push: skipped (needs SSH access to $MIRROR)"
|
||||||
|
echo " HOST1 setup: done ✅"
|
||||||
|
echo " Duration: $(format_duration $(( END - START )))"
|
||||||
|
echo ""
|
||||||
|
echo " ACTION NEEDED: install the key on $MIRROR:"
|
||||||
|
echo " ssh-copy-id -i ${SSH_KEY}.pub root@${MIRROR_IP}"
|
||||||
|
echo " Then click 'Push Conf' in Partnership tab, or run:"
|
||||||
|
echo " bash Partnership/partnership_onboard.sh --phase1-only --skip-ssh"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "━━━ Phase 1 — Conf Push ━━━"
|
echo "━━━ Phase 1 — Conf Push ━━━"
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ function vv_pt_nodes(): array {
|
|||||||
$onboardPhase = $isMe ? null
|
$onboardPhase = $isMe ? null
|
||||||
: (($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
|
: (($setupDb[$nodeIdUpper . '_PHASE2_DONE'] ?? '') === 'true' ? 2
|
||||||
: (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0));
|
: (($setupDb[$nodeIdUpper . '_PHASE1_DONE'] ?? '') === 'true' ? 1 : 0));
|
||||||
|
// key_ready: local key generated but not yet installed on HOST2 (SSH pending manual step)
|
||||||
|
$keyReady = !$isMe && ($setupDb[$nodeIdUpper . '_KEY_READY'] ?? '') === 'true';
|
||||||
// For self: local setup complete flag (set by partnership_manager --onboard --local-only)
|
// For self: local setup complete flag (set by partnership_manager --onboard --local-only)
|
||||||
$localDone = $isMe && ($setupDb[$nodeIdUpper . '_LOCAL_DONE'] ?? '') === 'true';
|
$localDone = $isMe && ($setupDb[$nodeIdUpper . '_LOCAL_DONE'] ?? '') === 'true';
|
||||||
|
|
||||||
@@ -172,6 +174,7 @@ function vv_pt_nodes(): array {
|
|||||||
'partnership' => $ptDb,
|
'partnership' => $ptDb,
|
||||||
'system' => $system,
|
'system' => $system,
|
||||||
'onboard_phase' => $onboardPhase,
|
'onboard_phase' => $onboardPhase,
|
||||||
|
'key_ready' => $keyReady,
|
||||||
'local_done' => $localDone,
|
'local_done' => $localDone,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,16 @@ function vvPtOnboard(btn) {
|
|||||||
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Onboard (Mirror)'; }, 4000));
|
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Onboard (Mirror)'; }, 4000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function vvPtPushConf(btn, hostId) {
|
||||||
|
if (!confirm(`Push conf to ${hostId}?\n\nAssumes SSH key is already installed on ${hostId}.\nSkips key generation/install, goes straight to conf push + local setup.`)) return;
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = '⟳ Pushing…';
|
||||||
|
_vvPtRun('Partnership/partnership_onboard.sh', '--phase1-only --skip-ssh')
|
||||||
|
.then(d => { if (!d.ok) alert('Failed: ' + (d.error ?? 'Unknown error')); })
|
||||||
|
.catch(e => alert('Error: ' + e))
|
||||||
|
.finally(() => setTimeout(() => { btn.disabled = false; btn.textContent = '▶ Push Conf (key installed)'; }, 4000));
|
||||||
|
}
|
||||||
|
|
||||||
function vvPtLocalSetup(btn) {
|
function vvPtLocalSetup(btn) {
|
||||||
if (!confirm('Complete HOST1 local setup?\n\nRuns FolderView3 integration and marks HOST1 as locally ready.\nDoes not require HOST2 to be online.')) return;
|
if (!confirm('Complete HOST1 local setup?\n\nRuns FolderView3 integration and marks HOST1 as locally ready.\nDoes not require HOST2 to be online.')) return;
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
@@ -245,7 +255,8 @@ function _nodeCard(node) {
|
|||||||
: '')
|
: '')
|
||||||
: (phase === 2 ? '' :
|
: (phase === 2 ? '' :
|
||||||
phase === 1 ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">⏳ SSH ready · awaiting onboard</div>` :
|
phase === 1 ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">⏳ SSH ready · awaiting onboard</div>` :
|
||||||
`<div style="font-size:10px;color:#555;margin-bottom:6px;padding:2px 6px;background:#111;border:1px solid #222;border-radius:3px;">○ Not yet provisioned</div>`);
|
node.key_ready ? `<div style="font-size:10px;color:#ff9800;margin-bottom:6px;padding:2px 6px;background:#1a1200;border:1px solid #3a2800;border-radius:3px;">🔑 Key generated · install on HOST2 then Push Conf</div>` :
|
||||||
|
`<div style="font-size:10px;color:#555;margin-bottom:6px;padding:2px 6px;background:#111;border:1px solid #222;border-radius:3px;">○ Not yet provisioned</div>`);
|
||||||
|
|
||||||
let body = '';
|
let body = '';
|
||||||
|
|
||||||
@@ -328,9 +339,15 @@ function _renderActions(nodes, cfg) {
|
|||||||
if (phase === 0) {
|
if (phase === 0) {
|
||||||
// Not yet provisioned — offer Phase 1 (safe before HOST2 has Varaverk)
|
// Not yet provisioned — offer Phase 1 (safe before HOST2 has Varaverk)
|
||||||
html += `<button class="vv-pt-action-btn run" onclick="vvPtPhase1(this, '${remote.id}')"
|
html += `<button class="vv-pt-action-btn run" onclick="vvPtPhase1(this, '${remote.id}')"
|
||||||
title="SSH key exchange + push master.conf to ${remote.hostname}. Safe to run before ${remote.id} has Varaverk installed.">
|
title="Generate SSH key + install on ${remote.hostname} + push conf. First run needs terminal access to ${remote.hostname} for password — subsequent runs auto-detect the installed key.">
|
||||||
▶ Phase 1: SSH + Conf Push
|
▶ Phase 1: SSH + Conf Push
|
||||||
</button>`;
|
</button>`;
|
||||||
|
if (remote.key_ready) {
|
||||||
|
html += `<button class="vv-pt-action-btn run" onclick="vvPtPushConf(this, '${remote.id}')"
|
||||||
|
title="Key is already installed on ${remote.hostname} — push conf files and complete Phase 1 without re-running SSH setup">
|
||||||
|
▶ Push Conf (key installed)
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
html += `<button class="vv-pt-action-btn info" onclick="vvPtOnboard(this)"
|
html += `<button class="vv-pt-action-btn info" onclick="vvPtOnboard(this)"
|
||||||
title="Run full onboard (Phase 1 + 2 together) — requires ${remote.id} to already have Varaverk installed"
|
title="Run full onboard (Phase 1 + 2 together) — requires ${remote.id} to already have Varaverk installed"
|
||||||
style="opacity:.5;">
|
style="opacity:.5;">
|
||||||
|
|||||||
Reference in New Issue
Block a user