Исправление синтаксической ошибки в molecule_executor.py и обновление k8s preset'ов
- Исправлена незакрытая скобка в _build_test_command (строка 745) - Добавлена поддержка k8s preset'ов: выполнение create_k8s_cluster.py перед create.yml - Обновлены образы в k8s preset'ах: заменен недоступный ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy на inecs/ansible-lab:ubuntu22-latest - Обновлены preset'ы в базе данных через SQL - Обновлены файлы: k8s-single.yml, k8s-multi.yml, k8s-istio-full.yml
This commit is contained in:
@@ -4,39 +4,17 @@
|
||||
{% block page_title %}Preset: {{ preset.name }}{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
<div class="btn-group">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success btn-sm"
|
||||
onclick="startPresetTest()"
|
||||
title="Запустить тест preset'а"
|
||||
>
|
||||
<i class="fas fa-play me-2"></i>
|
||||
Запустить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning btn-sm"
|
||||
onclick="stopPresetTest()"
|
||||
title="Остановить тест"
|
||||
id="stop-btn"
|
||||
style="display: none;"
|
||||
>
|
||||
<i class="fas fa-stop me-2"></i>
|
||||
Остановить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-info btn-sm"
|
||||
onclick="restartPresetTest()"
|
||||
title="Перезапустить тест"
|
||||
id="restart-btn"
|
||||
style="display: none;"
|
||||
>
|
||||
<i class="fas fa-redo me-2"></i>
|
||||
Перезапустить
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success btn-sm"
|
||||
onclick="openPresetTestModal()"
|
||||
title="Запустить тест preset'а"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#presetTestModal"
|
||||
>
|
||||
<i class="fas fa-play me-2"></i>
|
||||
Запустить
|
||||
</button>
|
||||
<a href="/presets/{{ preset.name }}/edit?category={{ preset.category }}" class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-edit me-2"></i>
|
||||
Редактировать
|
||||
@@ -49,7 +27,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-8">
|
||||
<div class="col-12">
|
||||
<!-- Информация о preset'е -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
@@ -83,7 +61,7 @@
|
||||
<div class="mb-3">
|
||||
<strong>Хосты:</strong>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<table class="table table-sm table-bordered w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Имя</th>
|
||||
@@ -144,24 +122,65 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-lg-4">
|
||||
<!-- Логи тестирования -->
|
||||
<div class="card" id="test-logs-card" style="display: none;">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Логи тестирования</h5>
|
||||
</div>
|
||||
|
||||
<!-- Модальное окно для тестирования preset'а -->
|
||||
<div class="modal fade" id="presetTestModal" tabindex="-1" aria-labelledby="presetTestModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-fullscreen">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="presetTestModalLabel">
|
||||
<i class="fas fa-vial me-2"></i>
|
||||
Тестирование preset'а: {{ preset.name }}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть" onclick="stopPresetTest()"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="log-container" id="preset-test-logs">
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="fas fa-spinner fa-spin fa-2x mb-3"></i>
|
||||
<p>Подключение к серверу...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-light"
|
||||
onclick="clearTestLogs()"
|
||||
class="btn btn-warning"
|
||||
onclick="stopPresetTest()"
|
||||
id="modal-stop-btn"
|
||||
title="Остановить тест"
|
||||
>
|
||||
<i class="fas fa-stop me-2"></i>
|
||||
Остановить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-info"
|
||||
onclick="restartPresetTest()"
|
||||
id="modal-restart-btn"
|
||||
title="Перезапустить тест"
|
||||
>
|
||||
<i class="fas fa-redo me-2"></i>
|
||||
Перезапустить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
onclick="clearPresetTestLogs()"
|
||||
title="Очистить логи"
|
||||
>
|
||||
<i class="fas fa-trash"></i>
|
||||
<i class="fas fa-trash me-2"></i>
|
||||
Очистить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
onclick="stopPresetTest()"
|
||||
>
|
||||
Закрыть
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="log-container" id="test-logs" style="height: 600px; overflow-y: auto; background: #1e1e1e; color: #d4d4d4; padding: 1rem; font-family: 'Courier New', monospace; font-size: 0.75rem;">
|
||||
<!-- Логи будут добавлены через WebSocket -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -172,31 +191,59 @@
|
||||
<script>
|
||||
let testWebSocket = null;
|
||||
let testRunning = false;
|
||||
let presetTestModal = null;
|
||||
|
||||
function startPresetTest() {
|
||||
// Инициализация модального окна
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
presetTestModal = new bootstrap.Modal(document.getElementById('presetTestModal'));
|
||||
|
||||
// Обработка закрытия модального окна
|
||||
document.getElementById('presetTestModal').addEventListener('hidden.bs.modal', function() {
|
||||
stopPresetTest();
|
||||
});
|
||||
});
|
||||
|
||||
function openPresetTestModal() {
|
||||
if (testRunning) {
|
||||
alert('Тест уже запущен');
|
||||
return;
|
||||
}
|
||||
|
||||
// Показываем логи
|
||||
const logsCard = document.getElementById('test-logs-card');
|
||||
const logsContainer = document.getElementById('test-logs');
|
||||
logsCard.style.display = 'block';
|
||||
// Очищаем логи
|
||||
const logsContainer = document.getElementById('preset-test-logs');
|
||||
logsContainer.innerHTML = '<div class="text-center text-muted py-5"><i class="fas fa-spinner fa-spin fa-2x mb-3"></i><p>Подключение к серверу...</p></div>';
|
||||
|
||||
// Показываем модальное окно
|
||||
presetTestModal.show();
|
||||
|
||||
// Запускаем тест
|
||||
setTimeout(() => {
|
||||
startPresetTest();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function startPresetTest() {
|
||||
if (testRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logsContainer = document.getElementById('preset-test-logs');
|
||||
logsContainer.innerHTML = '';
|
||||
|
||||
// Показываем кнопки управления
|
||||
document.getElementById('stop-btn').style.display = 'inline-block';
|
||||
document.getElementById('restart-btn').style.display = 'inline-block';
|
||||
document.getElementById('modal-stop-btn').disabled = false;
|
||||
document.getElementById('modal-restart-btn').disabled = false;
|
||||
|
||||
testRunning = true;
|
||||
|
||||
// Создаем WebSocket подключение
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const ws = new WebSocket(`${protocol}//${window.location.host}/ws/preset/test/{{ preset.name }}?category={{ preset.category }}`);
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws/preset/test/{{ preset.name }}?category={{ preset.category }}`;
|
||||
const ws = new WebSocket(wsUrl);
|
||||
testWebSocket = ws;
|
||||
|
||||
ws.onopen = () => {
|
||||
addLog('info', '🔌 Подключено к серверу');
|
||||
ws.send(JSON.stringify({
|
||||
action: 'start',
|
||||
preset_name: '{{ preset.name }}',
|
||||
@@ -205,64 +252,195 @@ function startPresetTest() {
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.type === 'log' || data.type === 'info') {
|
||||
const logLine = document.createElement('div');
|
||||
logLine.className = `log-line log-${data.level || 'info'}`;
|
||||
logLine.textContent = data.data;
|
||||
logsContainer.appendChild(logLine);
|
||||
logsContainer.scrollTop = logsContainer.scrollHeight;
|
||||
} else if (data.type === 'error') {
|
||||
const errorLine = document.createElement('div');
|
||||
errorLine.className = 'log-line log-error';
|
||||
errorLine.textContent = data.data;
|
||||
logsContainer.appendChild(errorLine);
|
||||
} else if (data.type === 'complete') {
|
||||
const completeLine = document.createElement('div');
|
||||
completeLine.className = 'log-line log-info';
|
||||
completeLine.textContent = data.data || '✅ Тестирование завершено';
|
||||
logsContainer.appendChild(completeLine);
|
||||
testRunning = false;
|
||||
document.getElementById('stop-btn').style.display = 'none';
|
||||
document.getElementById('restart-btn').style.display = 'none';
|
||||
ws.close();
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
if (data.type === 'log' || data.type === 'info') {
|
||||
addLog(data.level || 'info', data.data);
|
||||
} else if (data.type === 'error') {
|
||||
addLog('error', data.data);
|
||||
} else if (data.type === 'complete') {
|
||||
addLog('info', data.data || '✅ Тестирование завершено');
|
||||
testRunning = false;
|
||||
document.getElementById('modal-stop-btn').disabled = true;
|
||||
document.getElementById('modal-restart-btn').disabled = false;
|
||||
ws.close();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Ошибка парсинга сообщения:', e);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
const errorLine = document.createElement('div');
|
||||
errorLine.className = 'log-line log-error';
|
||||
errorLine.textContent = `❌ Ошибка подключения: ${error}`;
|
||||
logsContainer.appendChild(errorLine);
|
||||
addLog('error', `❌ Ошибка подключения WebSocket`);
|
||||
testRunning = false;
|
||||
document.getElementById('modal-stop-btn').disabled = true;
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
testRunning = false;
|
||||
console.log('WebSocket закрыт');
|
||||
if (testWebSocket === ws) {
|
||||
testWebSocket = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function stopPresetTest() {
|
||||
if (testWebSocket && testWebSocket.readyState === WebSocket.OPEN) {
|
||||
testWebSocket.send(JSON.stringify({ action: 'stop' }));
|
||||
addLog('info', '⏹️ Остановка тестирования...');
|
||||
}
|
||||
if (testWebSocket) {
|
||||
testWebSocket.close();
|
||||
testWebSocket = null;
|
||||
}
|
||||
testRunning = false;
|
||||
document.getElementById('stop-btn').style.display = 'none';
|
||||
document.getElementById('restart-btn').style.display = 'none';
|
||||
document.getElementById('modal-stop-btn').disabled = true;
|
||||
document.getElementById('modal-restart-btn').disabled = false;
|
||||
}
|
||||
|
||||
function restartPresetTest() {
|
||||
stopPresetTest();
|
||||
const logsContainer = document.getElementById('preset-test-logs');
|
||||
logsContainer.innerHTML = '';
|
||||
setTimeout(() => {
|
||||
startPresetTest();
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function clearTestLogs() {
|
||||
document.getElementById('test-logs').innerHTML = '';
|
||||
function clearPresetTestLogs() {
|
||||
const logsContainer = document.getElementById('preset-test-logs');
|
||||
logsContainer.innerHTML = '';
|
||||
}
|
||||
|
||||
function addLog(level, message) {
|
||||
const logsContainer = document.getElementById('preset-test-logs');
|
||||
const logLine = document.createElement('div');
|
||||
logLine.className = `log-line log-${level}`;
|
||||
|
||||
// Цвета для разных уровней логов
|
||||
const colors = {
|
||||
'error': '#f48771',
|
||||
'warning': '#d19a66',
|
||||
'info': '#61afef',
|
||||
'success': '#98c379',
|
||||
'debug': '#abb2bf'
|
||||
};
|
||||
|
||||
logLine.style.color = colors[level] || colors['debug'];
|
||||
logLine.textContent = message;
|
||||
logsContainer.appendChild(logLine);
|
||||
logsContainer.scrollTop = logsContainer.scrollHeight;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Стили для модального окна тестирования preset'а - как у модального окна логов сборки */
|
||||
#presetTestModal .modal-dialog {
|
||||
margin: 0 !important;
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
height: 100vh !important;
|
||||
max-height: 100vh !important;
|
||||
}
|
||||
|
||||
#presetTestModal .modal-content {
|
||||
height: 100vh !important;
|
||||
max-height: 100vh !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
#presetTestModal .modal-header {
|
||||
flex-shrink: 0 !important;
|
||||
padding: 1rem !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
#presetTestModal .modal-body {
|
||||
flex: 1 1 0 !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
width: 100% !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
height: auto !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
#preset-test-logs {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
box-sizing: border-box !important;
|
||||
overflow: auto !important;
|
||||
background: #1e1e1e !important;
|
||||
color: #d4d4d4 !important;
|
||||
padding: 1rem !important;
|
||||
font-family: 'Courier New', monospace !important;
|
||||
font-size: 0.875rem !important;
|
||||
white-space: pre !important;
|
||||
word-wrap: normal !important;
|
||||
overflow-wrap: normal !important;
|
||||
}
|
||||
|
||||
#presetTestModal .modal-footer {
|
||||
flex-shrink: 0 !important;
|
||||
padding: 1rem !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Дополнительные стили для гарантии полной высоты */
|
||||
#presetTestModal.show .modal-body,
|
||||
#presetTestModal.showing .modal-body {
|
||||
height: calc(100vh - 180px) !important;
|
||||
min-height: calc(100vh - 180px) !important;
|
||||
max-height: calc(100vh - 180px) !important;
|
||||
}
|
||||
|
||||
#presetTestModal.show #preset-test-logs,
|
||||
#presetTestModal.showing #preset-test-logs {
|
||||
height: calc(100vh - 180px) !important;
|
||||
min-height: calc(100vh - 180px) !important;
|
||||
max-height: calc(100vh - 180px) !important;
|
||||
}
|
||||
|
||||
/* Стили для строк логов */
|
||||
#preset-test-logs .log-line {
|
||||
margin-bottom: 0.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#preset-test-logs .log-error {
|
||||
color: #f48771 !important;
|
||||
}
|
||||
|
||||
#preset-test-logs .log-warning {
|
||||
color: #d19a66 !important;
|
||||
}
|
||||
|
||||
#preset-test-logs .log-info {
|
||||
color: #61afef !important;
|
||||
}
|
||||
|
||||
#preset-test-logs .log-success {
|
||||
color: #98c379 !important;
|
||||
}
|
||||
|
||||
#preset-test-logs .log-debug {
|
||||
color: #abb2bf !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user