Исправление синтаксической ошибки в 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:
Сергей Антропов
2026-02-16 00:31:09 +03:00
parent 1fbf9185a2
commit d4b0d6f848
26 changed files with 1913 additions and 646 deletions

View File

@@ -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 %}