Files
fission-console/client-console/ui/js/init.js
T

181 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* init.js — инициализация namespace, утилиты таймера и статуса */
var _initPolling = false;
var _initTickBusy = false;
var _initStartedAt = 0;
function initLog(msg) {
var el = document.getElementById('init-log');
var ts = new Date().toLocaleTimeString('ru-RU');
el.textContent += '[' + ts + '] ' + msg + '\n';
el.scrollTop = el.scrollHeight;
}
function formatSeconds(totalSeconds) {
totalSeconds = Math.max(0, Number(totalSeconds) || 0);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
if (minutes <= 0) return seconds + 'с';
return minutes + 'м ' + seconds + 'с';
}
function setInitElapsed(seconds) {
document.getElementById('init-elapsed').textContent = formatSeconds(seconds);
}
function setInitCurrentStage(text) {
document.getElementById('init-current-stage').textContent = text || 'Ожидание старта';
}
function setBox(id, text) {
var el = document.getElementById(id);
if (!el) return;
if (!text) {
el.style.display = 'none';
el.textContent = '';
return;
}
el.style.display = 'block';
el.textContent = text;
}
function missingKeys(obj) {
return Object.keys(obj || {}).filter(function (key) {
return !obj[key];
});
}
function firstPendingStage(stages) {
var list = stages || [];
for (var i = 0; i < list.length; i++) {
if (!list[i].done) return list[i].name;
}
return list.length ? list[list.length - 1].name : 'Ожидание старта';
}
function describeInitReason(debug) {
if (!debug) return 'Собираем диагностические данные по namespace и control-plane...';
if (!debug.exists) return 'Ещё не создан Kubernetes namespace пользователя.';
if (!debug.active) return 'Namespace создан, но Kubernetes ещё не перевёл его в состояние Active.';
var preparation = debug.preparation || {};
if (!preparation.managedByConsole) return 'Namespace существует, но ещё не помечен как managed-by=fission-console.';
if (!preparation.watcherLabel) return 'Namespace ещё не помечен label fission.io/managed=true для NSWatcher.';
var missingSAs = missingKeys(preparation.serviceAccounts);
if (missingSAs.length) return 'Не готовы bootstrap service accounts: ' + missingSAs.join(', ') + '.';
var missingRBs = missingKeys(preparation.roleBindings);
if (missingRBs.length) return 'Не готовы bootstrap role bindings: ' + missingRBs.join(', ') + '.';
var control = debug.controlPlane || {};
if (!control.executor || !control.executor.ready) {
return 'Executor ещё не ready. Возможна задержка из-за cold start, image pull или specialization.';
}
if (!control.router || !control.router.ready) {
return 'Router ещё не ready. HTTP маршрутизация пока не активна.';
}
return 'Ожидаем финальное подтверждение готовности control-plane.';
}
function summarizeInit(status, debug) {
var done = status && status.stages ? status.stages.filter(function (s) { return s.done; }).length : 0;
var namespace = debug && debug.namespace ? debug.namespace : 'не определён';
return 'Namespace: ' + namespace + '. Завершено шагов: ' + done + '/3.';
}
function explainDelay(seconds) {
if (seconds < 5) return 'Обычная операция без заметной задержки.';
if (seconds < 10) return 'Вероятен cold start: прогрев пула, image pull или ожидание specialization.';
return 'Задержка выше нормы: возможны cold start, specialization или ожидание готовности control-plane.';
}
function startTimedStatus(startText, waitingPrefix, waitingHint) {
var startedAt = Date.now();
showStatus(startText, '');
var timer = setInterval(function () {
var elapsed = Math.max(1, Math.floor((Date.now() - startedAt) / 1000));
var text = waitingPrefix + ' ' + formatSeconds(elapsed);
if (waitingHint) text += ' · ' + waitingHint(elapsed);
showStatus(text, '');
}, 1000);
return {
stop: function (message, kind) {
clearInterval(timer);
var elapsed = Math.max(0, Math.round((Date.now() - startedAt) / 1000));
var text = message;
if (message) text += ' за ' + formatSeconds(elapsed);
showStatus(text, kind || '');
}
};
}
function renderInitStages(stages) {
var el = document.getElementById('init-stages');
el.innerHTML = stages.map(function (s) {
var icon = s.done ? '✅' : '⏳';
var color = s.done ? '#4caf50' : '#8bc7ff';
return '<div style="font-size:13px; color:' + color + ';">' + icon + '&nbsp;' + s.name + '</div>';
}).join('');
}
async function pollNSStatus() {
if (_initPolling) return;
_initPolling = true;
_initTickBusy = false;
_initStartedAt = Date.now();
var overlay = document.getElementById('init-overlay');
overlay.style.display = 'flex';
document.getElementById('init-log').textContent = '';
setInitElapsed(0);
setInitCurrentStage('Подготовка запуска');
setBox('init-summary', 'Проверяем состояние namespace и control-plane...');
setBox('init-reason', 'Если пользователь новый, возможны cold start и создание bootstrap ресурсов.');
setBox('init-error', '');
initLog('Запуск инициализации...');
var attempt = 0;
var maxAttempts = 60; // 5 минут
var interval = setInterval(async function () {
if (_initTickBusy) return;
_initTickBusy = true;
attempt++;
try {
var elapsed = Math.floor((Date.now() - _initStartedAt) / 1000);
setInitElapsed(elapsed);
var statusResp = await fetch(API_BASE + '/ns/status', { headers: authHeaders() });
var debugResp = await fetch(API_BASE + '/ns/debug', { headers: authHeaders() });
var d = await statusResp.json();
var dbg = await debugResp.json().catch(function () { return null; });
if (d.stages) renderInitStages(d.stages);
setInitCurrentStage(firstPendingStage(d.stages));
setBox('init-summary', summarizeInit(d, dbg));
setBox('init-reason', describeInitReason(dbg));
setBox('init-error', '');
var done = d.stages ? d.stages.filter(function (s) { return s.done; }).length : 0;
initLog('Шагов завершено: ' + done + '/3');
if (d.ready) {
initLog('✅ Готово! Загружаем консоль...');
clearInterval(interval);
_initPolling = false;
_initTickBusy = false;
overlay.style.display = 'none';
reloadAll();
return;
}
} catch (e) {
setBox('init-error', 'Последняя ошибка: ' + e.message);
initLog('Ошибка опроса: ' + e.message);
} finally {
_initTickBusy = false;
}
if (attempt >= maxAttempts) {
clearInterval(interval);
_initPolling = false;
_initTickBusy = false;
setBox('init-error', 'Превышено время ожидания. Чаще всего это bootstrap RBAC, control-plane readiness или холодный старт.');
initLog('⚠️ Превышено время ожидания. Попробуйте обновить страницу.');
}
}, 5000);
}