feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
180
app/services/lint_service.py
Normal file
180
app/services/lint_service.py
Normal file
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
Сервис для проверки синтаксиса ролей (ansible-lint)
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Optional, AsyncGenerator, Dict, List
|
||||
from datetime import datetime
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
class LintService:
|
||||
"""Проверка синтаксиса ролей через ansible-lint"""
|
||||
|
||||
def __init__(self):
|
||||
self.project_root = settings.PROJECT_ROOT
|
||||
self.roles_dir = self.project_root / "roles"
|
||||
self.lint_config = self.project_root / ".ansible-lint"
|
||||
|
||||
async def lint_role(
|
||||
self,
|
||||
role_name: Optional[str] = None,
|
||||
stream: bool = False
|
||||
) -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
Проверка синтаксиса роли через ansible-lint
|
||||
|
||||
Args:
|
||||
role_name: Имя роли (опционально, если None - проверяются все роли)
|
||||
stream: Если True, возвращает генератор строк
|
||||
|
||||
Yields:
|
||||
Строки вывода команды
|
||||
"""
|
||||
# Расшифровка vault файлов перед линтингом
|
||||
yield "🔓 Расшифровка vault файлов...\n"
|
||||
await self._decrypt_vault_files()
|
||||
|
||||
if role_name:
|
||||
role_path = self.roles_dir / role_name
|
||||
if not role_path.exists():
|
||||
yield f"❌ Роль '{role_name}' не найдена\n"
|
||||
return
|
||||
|
||||
yield f"🔍 Проверка синтаксиса роли: {role_name}\n"
|
||||
cmd = [
|
||||
"ansible-lint",
|
||||
str(role_path),
|
||||
"--config-file", str(self.lint_config)
|
||||
]
|
||||
else:
|
||||
yield "🔍 Проверка синтаксиса всех ролей...\n"
|
||||
cmd = [
|
||||
"ansible-lint",
|
||||
str(self.roles_dir),
|
||||
"--config-file", str(self.lint_config)
|
||||
]
|
||||
|
||||
# Запуск в Docker контейнере для изоляции
|
||||
docker_cmd = [
|
||||
"docker", "run", "--rm",
|
||||
"--name", f"ansible-lint-{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
||||
"-v", f"{self.project_root}:/workspace",
|
||||
"-w", "/workspace",
|
||||
"-e", "ANSIBLE_FORCE_COLOR=1",
|
||||
"inecs/ansible-lab:ansible-controller-latest",
|
||||
"bash", "-c", " ".join(cmd) + " || true"
|
||||
]
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*docker_cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
cwd=str(self.project_root)
|
||||
)
|
||||
|
||||
async for line in process.stdout:
|
||||
yield line.decode('utf-8', errors='replace')
|
||||
|
||||
await process.wait()
|
||||
|
||||
# Шифрование vault файлов после линтинга
|
||||
yield "\n🔒 Шифрование vault файлов...\n"
|
||||
await self._encrypt_vault_files()
|
||||
|
||||
if process.returncode == 0:
|
||||
yield "\n✅ Линтинг завершен успешно\n"
|
||||
else:
|
||||
yield f"\n⚠️ Линтинг завершен с предупреждениями (код: {process.returncode})\n"
|
||||
|
||||
async def _decrypt_vault_files(self) -> bool:
|
||||
"""Расшифровка vault файлов"""
|
||||
vault_dir = self.project_root / "vault"
|
||||
if not vault_dir.exists():
|
||||
return True
|
||||
|
||||
vault_password_file = vault_dir / ".vault"
|
||||
if not vault_password_file.exists():
|
||||
return True
|
||||
|
||||
vault_files = list(vault_dir.glob("*.yml"))
|
||||
if not vault_files:
|
||||
return True
|
||||
|
||||
try:
|
||||
for vault_file in vault_files:
|
||||
with open(vault_file, 'rb') as f:
|
||||
content = f.read(100)
|
||||
if b'$ANSIBLE_VAULT' not in content:
|
||||
continue
|
||||
|
||||
cmd = [
|
||||
"ansible-vault", "decrypt",
|
||||
str(vault_file),
|
||||
"--vault-password-file", str(vault_password_file)
|
||||
]
|
||||
result = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=str(self.project_root)
|
||||
)
|
||||
await result.wait()
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
async def _encrypt_vault_files(self) -> bool:
|
||||
"""Шифрование vault файлов"""
|
||||
vault_dir = self.project_root / "vault"
|
||||
if not vault_dir.exists():
|
||||
return True
|
||||
|
||||
vault_password_file = vault_dir / ".vault"
|
||||
if not vault_password_file.exists():
|
||||
return True
|
||||
|
||||
vault_files = list(vault_dir.glob("*.yml"))
|
||||
if not vault_files:
|
||||
return True
|
||||
|
||||
try:
|
||||
for vault_file in vault_files:
|
||||
with open(vault_file, 'rb') as f:
|
||||
content = f.read(100)
|
||||
if b'$ANSIBLE_VAULT' in content:
|
||||
continue
|
||||
|
||||
cmd = [
|
||||
"ansible-vault", "encrypt",
|
||||
str(vault_file),
|
||||
"--vault-password-file", str(vault_password_file)
|
||||
]
|
||||
result = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
cwd=str(self.project_root)
|
||||
)
|
||||
await result.wait()
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def detect_log_level(self, line: str) -> str:
|
||||
"""Определение уровня лога из строки"""
|
||||
line_lower = line.lower()
|
||||
if any(word in line_lower for word in ["error", "failed", "fatal"]):
|
||||
return "error"
|
||||
elif any(word in line_lower for word in ["warning", "warn"]):
|
||||
return "warning"
|
||||
elif any(word in line_lower for word in ["passed", "ok"]):
|
||||
return "info"
|
||||
else:
|
||||
return "debug"
|
||||
Reference in New Issue
Block a user