commit
aff9419e1c
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
## [0.22.0] - 2025-09-03
|
||||
### Added
|
||||
- Se han mejorado el control de errores respecto a ogRepository
|
||||
- Se ha añadido una validacion para impedir el deploy de imagenes que no esten en "success".
|
||||
|
||||
---
|
||||
## [0.21.0] - 2025-08-28
|
||||
### Added
|
||||
- Se ha incluido una integracion con el agente, para generar los scripts que se llaman al particionar. Nuevo servicio.
|
||||
|
|
|
@ -45,6 +45,10 @@ class DeployImageAction extends AbstractController
|
|||
*/
|
||||
public function __invoke(DeployImageInput $input, ImageImageRepository $image): JsonResponse
|
||||
{
|
||||
if ($image->getStatus() !== ImageStatus::SUCCESS) {
|
||||
return new BadRequestHttpException('Image is not ready to be deployed');
|
||||
}
|
||||
|
||||
$this->validator->validate($input);
|
||||
|
||||
$clientJobs = [];
|
||||
|
|
|
@ -48,19 +48,48 @@ abstract class AbstractOgRepositoryController extends AbstractController
|
|||
'timeout' => 30,
|
||||
]);
|
||||
|
||||
$this->logger->info('Sending HTTP request', [
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'params' => $params
|
||||
]);
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->request($method, $url, $params);
|
||||
return json_decode($response->getContent(), true);
|
||||
$content = $response->getContent();
|
||||
$decodedContent = json_decode($content, true);
|
||||
|
||||
$this->logger->info('HTTP response received successfully', [
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'status_code' => $response->getStatusCode(),
|
||||
'response_size' => strlen($content),
|
||||
'decoded_content' => $decodedContent
|
||||
]);
|
||||
|
||||
return $decodedContent;
|
||||
} catch (ClientExceptionInterface | ServerExceptionInterface $e) {
|
||||
$this->logger->error(sprintf('Client/Server error in request to %s: %s', $url, $e->getMessage()));
|
||||
$statusCode = $e->getResponse()?->getStatusCode() ?? Response::HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
$this->logger->error('Client/server error in HTTP request', [
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'error' => $e->getMessage(),
|
||||
'status_code' => $statusCode,
|
||||
'response_content' => $e->getResponse()?->getContent(false)
|
||||
]);
|
||||
|
||||
return [
|
||||
'code' => Response::HTTP_INTERNAL_SERVER_ERROR,
|
||||
'code' => $statusCode,
|
||||
'error' => $e->getMessage(),
|
||||
'details' => $e->getMessage(),
|
||||
'details' => $e->getResponse()?->getContent(false) ?? $e->getMessage(),
|
||||
];
|
||||
} catch (TransportExceptionInterface $e) {
|
||||
$this->logger->error(sprintf('Transport error in request to %s: %s', $url, $e->getMessage()));
|
||||
$this->logger->error('Transport error in HTTP request', [
|
||||
'method' => $method,
|
||||
'url' => $url,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return [
|
||||
'code' => Response::HTTP_INTERNAL_SERVER_ERROR,
|
||||
|
|
|
@ -34,10 +34,26 @@ class BackupImageAction extends AbstractOgRepositoryController
|
|||
$image = $imageImageRepository->getImage();
|
||||
$repository = $imageImageRepository->getRepository();
|
||||
|
||||
$this->validateImageName($image);
|
||||
$this->logger->info('Create backup image', ['image' => $image->getName()]);
|
||||
|
||||
$content = $this->createBackupRequest($input, $imageImageRepository, $repository);
|
||||
$this->handleBackupResponse($content);
|
||||
|
||||
$this->processBackupSuccess($input, $image, $repository, $imageImageRepository, $content);
|
||||
|
||||
return new JsonResponse(data: $content, status: Response::HTTP_OK);
|
||||
}
|
||||
|
||||
private function validateImageName(Image $image): void
|
||||
{
|
||||
if (!$image->getName()) {
|
||||
throw new BadRequestHttpException('Name is required');
|
||||
}
|
||||
}
|
||||
|
||||
private function createBackupRequest(BackupImageInput $input, ImageImageRepository $imageImageRepository, ImageRepository $repository): array
|
||||
{
|
||||
$params = [
|
||||
'json' => [
|
||||
'ID_img' => $imageImageRepository->getImageFullsum(),
|
||||
|
@ -48,16 +64,85 @@ class BackupImageAction extends AbstractOgRepositoryController
|
|||
]
|
||||
];
|
||||
|
||||
$this->logger->info('Create backup image', ['image' => $image->getName()]);
|
||||
return $this->createRequest('PUT', 'http://'.$repository->getIp().':8006/ogrepository/v1/repo/images', $params);
|
||||
}
|
||||
|
||||
$repository = $imageImageRepository->getRepository();
|
||||
|
||||
$content = $this->createRequest('PUT', 'http://'.$repository->getIp().':8006/ogrepository/v1/repo/images', $params);
|
||||
|
||||
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) {
|
||||
throw new BadRequestHttpException('Error backing up image: ' . $content['error'] . ' - ' . $content['details']);
|
||||
private function handleBackupResponse(array $content): void
|
||||
{
|
||||
if (!isset($content['error'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$errorMessage = $this->extractErrorMessage($content);
|
||||
$errorCode = $content['code'] ?? Response::HTTP_BAD_REQUEST;
|
||||
|
||||
$this->throwAppropriateException($errorCode, $errorMessage);
|
||||
}
|
||||
|
||||
private function extractErrorMessage(array $content): string
|
||||
{
|
||||
$errorMessage = $content['error'];
|
||||
$errorDetails = $content['details'] ?? null;
|
||||
|
||||
if (!$errorDetails) {
|
||||
return $errorMessage;
|
||||
}
|
||||
|
||||
$extractedMessage = $this->extractMessageFromDetails($errorDetails);
|
||||
|
||||
return $extractedMessage ?: $errorMessage;
|
||||
}
|
||||
|
||||
private function extractMessageFromDetails($errorDetails): ?string
|
||||
{
|
||||
if (is_array($errorDetails)) {
|
||||
return $errorDetails['details'] ?? $errorDetails['message'] ?? $errorDetails['error'] ?? null;
|
||||
}
|
||||
|
||||
if (is_string($errorDetails)) {
|
||||
$parsed = $this->parseJsonString($errorDetails);
|
||||
if ($parsed !== null) {
|
||||
return $parsed['details'] ?? $parsed['message'] ?? $parsed['error'] ?? null;
|
||||
}
|
||||
|
||||
return !empty($errorDetails) ? $errorDetails : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function parseJsonString(string $jsonString): ?array
|
||||
{
|
||||
$cleanJson = trim($jsonString, " \t\n\r\0\x0B");
|
||||
$decoded = json_decode($cleanJson, true);
|
||||
|
||||
return (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) ? $decoded : null;
|
||||
}
|
||||
|
||||
private function throwAppropriateException(int $errorCode, string $errorMessage): void
|
||||
{
|
||||
if ($errorCode === Response::HTTP_INTERNAL_SERVER_ERROR) {
|
||||
throw new BadRequestHttpException('Error backing up image: ' . $errorMessage);
|
||||
}
|
||||
|
||||
if ($errorCode >= 400 && $errorCode < 500) {
|
||||
throw new BadRequestHttpException($errorMessage);
|
||||
}
|
||||
|
||||
if ($errorCode >= 500) {
|
||||
throw new BadRequestHttpException('Error backing up image: ' . $errorMessage);
|
||||
}
|
||||
|
||||
throw new BadRequestHttpException($errorMessage);
|
||||
}
|
||||
|
||||
private function processBackupSuccess(
|
||||
BackupImageInput $input,
|
||||
Image $image,
|
||||
ImageRepository $repository,
|
||||
ImageImageRepository $imageImageRepository,
|
||||
array $content
|
||||
): void {
|
||||
$inputData = [
|
||||
'imageName' => $image->getName(),
|
||||
'repositoryUuid' => $repository->getUuid(),
|
||||
|
@ -67,13 +152,16 @@ class BackupImageAction extends AbstractOgRepositoryController
|
|||
'remote_path' => $input->remotePath
|
||||
];
|
||||
|
||||
$this->createService->__invoke($image->getClient(), CommandTypes::BACKUP_IMAGE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData);
|
||||
$this->createService->__invoke(
|
||||
$image->getClient(),
|
||||
CommandTypes::BACKUP_IMAGE,
|
||||
TraceStatus::IN_PROGRESS,
|
||||
$content['job_id'],
|
||||
$inputData
|
||||
);
|
||||
|
||||
$imageImageRepository->setStatus(ImageStatus::BACKUP);
|
||||
$this->entityManager->persist($imageImageRepository);
|
||||
$this->entityManager->flush();
|
||||
|
||||
|
||||
return new JsonResponse(data: $content, status: Response::HTTP_OK);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
|||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
#[AsController]
|
||||
class ImportAction extends AbstractOgRepositoryController
|
||||
|
@ -30,20 +31,52 @@ class ImportAction extends AbstractOgRepositoryController
|
|||
* @throws ClientExceptionInterface
|
||||
*/
|
||||
public function __invoke(ImportImageRepositoryInput $input, ImageRepository $repository): JsonResponse
|
||||
{
|
||||
$this->validateRepositoryStatus($repository);
|
||||
|
||||
$image = $input->name;
|
||||
$imageEntity = $this->getOrCreateImageEntity($image);
|
||||
$this->validateImageNotExistsInRepository($imageEntity, $repository);
|
||||
|
||||
$this->logger->info('Creating aux files', ['image' => $image]);
|
||||
|
||||
$content = $this->createTorrentSumRequest($repository, $image);
|
||||
$this->handleTorrentSumResponse($content);
|
||||
|
||||
$imageImageRepositoryEntity = $this->createImageImageRepositoryEntity($imageEntity, $repository, $image);
|
||||
$this->entityManager->persist($imageImageRepositoryEntity);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->createService->__invoke(
|
||||
null,
|
||||
CommandTypes::CREATE_IMAGE_AUX_FILE,
|
||||
TraceStatus::IN_PROGRESS,
|
||||
$content['job_id'],
|
||||
[
|
||||
'imageName' => $image,
|
||||
'imageImageRepositoryUuid' => $imageImageRepositoryEntity->getUuid(),
|
||||
]
|
||||
);
|
||||
|
||||
return new JsonResponse(data: [], status: Response::HTTP_OK);
|
||||
}
|
||||
|
||||
private function validateRepositoryStatus(ImageRepository $repository): void
|
||||
{
|
||||
$content = $this->createRequest('GET', 'http://'.$repository->getIp(). ':8006/ogrepository/v1/status');
|
||||
|
||||
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
|
||||
throw new BadRequestHttpException('An error occurred while fetching the status: ' . $content['details']);
|
||||
}
|
||||
}
|
||||
|
||||
$image = $input->name;
|
||||
private function getOrCreateImageEntity(string $imageName): Image
|
||||
{
|
||||
$imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $imageName]);
|
||||
|
||||
$imageEntity = $this->entityManager->getRepository(Image::class)->findOneBy(['name' => $image]);
|
||||
|
||||
if (!$imageEntity){
|
||||
if (!$imageEntity) {
|
||||
$imageEntity = new Image();
|
||||
$imageEntity->setName($image);
|
||||
$imageEntity->setName($imageName);
|
||||
$imageEntity->setType('monolithic');
|
||||
$imageEntity->setRemotePc(false);
|
||||
$imageEntity->setIsGlobal(false);
|
||||
|
@ -51,65 +84,97 @@ class ImportAction extends AbstractOgRepositoryController
|
|||
$this->entityManager->persist($imageEntity);
|
||||
}
|
||||
|
||||
$imageImageRepositoryEntity = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['image' => $imageEntity, 'repository' => $repository]);
|
||||
return $imageEntity;
|
||||
}
|
||||
|
||||
if ($imageImageRepositoryEntity){
|
||||
private function validateImageNotExistsInRepository(Image $imageEntity, ImageRepository $repository): void
|
||||
{
|
||||
$imageImageRepositoryEntity = $this->entityManager->getRepository(ImageImageRepository::class)
|
||||
->findOneBy(['image' => $imageEntity, 'repository' => $repository]);
|
||||
|
||||
if ($imageImageRepositoryEntity) {
|
||||
throw new BadRequestHttpException('This image already exists in this repository');
|
||||
}
|
||||
}
|
||||
|
||||
$imageImageRepositoryEntity = new ImageImageRepository();
|
||||
$imageImageRepositoryEntity->setName($imageEntity->getName());
|
||||
$imageImageRepositoryEntity->setStatus(ImageStatus::AUX_FILES_PENDING);
|
||||
$imageImageRepositoryEntity->setImage($imageEntity);
|
||||
$imageImageRepositoryEntity->setRepository($repository);
|
||||
$imageImageRepositoryEntity->setVersion($this->extractVersionFromImageName($image));
|
||||
|
||||
$this->entityManager->persist($imageImageRepositoryEntity);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$this->logger->info('Creating aux files', ['image' => $image]);
|
||||
|
||||
private function createTorrentSumRequest(ImageRepository $repository, string $image): array
|
||||
{
|
||||
$params = [
|
||||
'json' => [
|
||||
'image' => $image.'.img'
|
||||
]
|
||||
];
|
||||
|
||||
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/torrentsum', $params);
|
||||
|
||||
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) {
|
||||
throw new BadRequestHttpException('Error importing image' . ' - ' . $content['error'] . ' - ' . $content['details']);
|
||||
}
|
||||
|
||||
$inputData = [
|
||||
'imageName' => $image,
|
||||
'imageImageRepositoryUuid' => $imageImageRepositoryEntity->getUuid(),
|
||||
];
|
||||
|
||||
$this->createService->__invoke(null, CommandTypes::CREATE_IMAGE_AUX_FILE, TraceStatus::IN_PROGRESS, $content['job_id'], $inputData);
|
||||
|
||||
return new JsonResponse(data: [], status: Response::HTTP_OK);
|
||||
return $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/torrentsum', $params);
|
||||
}
|
||||
|
||||
private function handleTorrentSumResponse(array $content): void
|
||||
{
|
||||
if (!isset($content['error'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$errorMessage = $this->extractErrorMessage($content);
|
||||
$errorCode = $content['code'] ?? Response::HTTP_BAD_REQUEST;
|
||||
|
||||
$this->throwAppropriateException($errorCode, $errorMessage);
|
||||
}
|
||||
|
||||
private function extractErrorMessage(array $content): string
|
||||
{
|
||||
$errorMessage = $content['error'];
|
||||
$errorDetails = $content['details'] ?? null;
|
||||
|
||||
if (!$errorDetails) {
|
||||
return $errorMessage;
|
||||
}
|
||||
|
||||
if (is_array($errorDetails)) {
|
||||
return $errorDetails['message'] ?? $errorDetails['error'] ?? $errorMessage;
|
||||
}
|
||||
|
||||
if (is_string($errorDetails) && !empty($errorDetails)) {
|
||||
return $errorDetails;
|
||||
}
|
||||
|
||||
return $errorMessage;
|
||||
}
|
||||
|
||||
private function throwAppropriateException(int $errorCode, string $errorMessage): void
|
||||
{
|
||||
if ($errorCode >= 400 && $errorCode < 500) {
|
||||
throw new BadRequestHttpException($errorMessage);
|
||||
}
|
||||
|
||||
if ($errorCode >= 500) {
|
||||
throw new HttpException($errorCode, $errorMessage);
|
||||
}
|
||||
|
||||
throw new BadRequestHttpException($errorMessage);
|
||||
}
|
||||
|
||||
private function createImageImageRepositoryEntity(Image $imageEntity, ImageRepository $repository, string $imageName): ImageImageRepository
|
||||
{
|
||||
$imageImageRepositoryEntity = new ImageImageRepository();
|
||||
$imageImageRepositoryEntity->setName($imageEntity->getName());
|
||||
$imageImageRepositoryEntity->setStatus(ImageStatus::AUX_FILES_PENDING);
|
||||
$imageImageRepositoryEntity->setImage($imageEntity);
|
||||
$imageImageRepositoryEntity->setRepository($repository);
|
||||
$imageImageRepositoryEntity->setVersion($this->extractVersionFromImageName($imageName));
|
||||
|
||||
return $imageImageRepositoryEntity;
|
||||
}
|
||||
|
||||
private function extractVersionFromImageName(string $imageName): int
|
||||
{
|
||||
// Buscar patrones como "_v2", "_v3", etc.
|
||||
if (preg_match('/_v(\d+)$/', $imageName, $matches)) {
|
||||
return (int) $matches[1];
|
||||
$patterns = ['/_v(\d+)$/', '/-v(\d+)$/', '/v(\d+)$/'];
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $imageName, $matches)) {
|
||||
return (int) $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Buscar patrones como "-v2", "-v3", etc.
|
||||
if (preg_match('/-v(\d+)$/', $imageName, $matches)) {
|
||||
return (int) $matches[1];
|
||||
}
|
||||
|
||||
// Buscar patrones como "v2", "v3" al final del nombre
|
||||
if (preg_match('/v(\d+)$/', $imageName, $matches)) {
|
||||
return (int) $matches[1];
|
||||
}
|
||||
|
||||
// Si no se encuentra ningún patrón de versión, devolver 1 por defecto
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -26,6 +26,12 @@ class ResponseController extends AbstractOgRepositoryController
|
|||
public function repositoryWebhook(Request $request): JsonResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true);
|
||||
|
||||
$this->logger->info('Webhook data received', [
|
||||
'payload' => $data,
|
||||
'raw_content' => $request->getContent(),
|
||||
'headers' => $request->headers->all()
|
||||
]);
|
||||
|
||||
if (!isset($data['job_id'])) {
|
||||
return $this->jsonResponseError('Invalid request', Response::HTTP_BAD_REQUEST);
|
||||
|
|
Loading…
Reference in New Issue