diff --git a/migrations/Version20250402060324.php b/migrations/Version20250402060324.php new file mode 100644 index 0000000..f3b99b7 --- /dev/null +++ b/migrations/Version20250402060324.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image ADD version INT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image DROP version'); + } +} diff --git a/migrations/Version20250402081107.php b/migrations/Version20250402081107.php new file mode 100644 index 0000000..e03a6f1 --- /dev/null +++ b/migrations/Version20250402081107.php @@ -0,0 +1,31 @@ +addSql('DROP INDEX UNIQ_IDENTIFIER_IMAGE_REPOSITORY ON image_image_repository'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_IMAGE_REPOSITORY ON image_image_repository (image_id, repository_id)'); + } +} diff --git a/migrations/Version20250402094550.php b/migrations/Version20250402094550.php new file mode 100644 index 0000000..fb6d618 --- /dev/null +++ b/migrations/Version20250402094550.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE image_image_repository ADD version INT DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE image_image_repository DROP version'); + } +} diff --git a/src/Controller/OgAgent/CreateImageAction.php b/src/Controller/OgAgent/CreateImageAction.php index 2a0d485..a98033b 100644 --- a/src/Controller/OgAgent/CreateImageAction.php +++ b/src/Controller/OgAgent/CreateImageAction.php @@ -56,10 +56,13 @@ class CreateImageAction extends AbstractController $repository = $image->getClient()->getRepository(); + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion(); + $imageImageRepository = new ImageImageRepository(); $imageImageRepository->setImage($image); $imageImageRepository->setRepository($repository); $imageImageRepository->setStatus(ImageStatus::IN_PROGRESS); + $imageImageRepository->setVersion($latestImageRepo ? $latestImageRepo->getVersion() + 1 : 1); $this->entityManager->persist($imageImageRepository); @@ -90,7 +93,7 @@ class CreateImageAction extends AbstractController try { $this->logger->info('Creating image', ['image' => $image->getId()]); - $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/opengnsys/CrearImagen', [ + $response = $this->httpClient->request('POST', 'https://'.$image->getClient()->getIp().':8000/CloningEngine/CrearImagen', [ 'verify_peer' => false, 'verify_host' => false, 'headers' => [ diff --git a/src/Controller/OgAgent/RunScriptAction.php b/src/Controller/OgAgent/RunScriptAction.php index ee840ff..170c91b 100644 --- a/src/Controller/OgAgent/RunScriptAction.php +++ b/src/Controller/OgAgent/RunScriptAction.php @@ -70,7 +70,12 @@ class RunScriptAction extends AbstractController $this->entityManager->persist($client); $this->entityManager->flush(); - $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, []); + $inputData = [ + 'script' => $input->script, + ]; + + + $this->createService->__invoke($client, CommandTypes::RUN_SCRIPT, TraceStatus::SUCCESS, $jobId, $inputData); } return new JsonResponse(data: [], status: Response::HTTP_OK); diff --git a/src/Controller/OgRepository/Image/RenameAction.php b/src/Controller/OgRepository/Image/RenameAction.php index 8d83633..d66ac9c 100644 --- a/src/Controller/OgRepository/Image/RenameAction.php +++ b/src/Controller/OgRepository/Image/RenameAction.php @@ -51,20 +51,27 @@ class RenameAction extends AbstractOgRepositoryController return new JsonResponse(data: ['error' => 'Image is not available in all repositories', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST); } + $image->setVersion($image->getVersion() + 1); + $this->entityManager->persist($image); + $conditional = false; - foreach ($repositories as $repository) { + + $latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion(); + $repoWithImage = $this->entityManager->getRepository(ImageImageRepository::class)->findBy(['image' => $image, 'version' => $latestImageRepo->getVersion()]); + + + foreach ($repoWithImage as $repository) { $params = [ 'json' => [ 'ID_img' => $repository->getImageFullsum(), - 'image_new_name' => $image->getName().'_bkp', + 'image_new_name' => $image->getName().'_v'.$image->getVersion(), ] ]; - $content = $this->createRequest('POST', 'http://'.$repository->getRepository()->getIp().':8006/ogrepository/v1/images/rename', $params); + $content = $this->createRequest('PUT', 'http://'.$repository->getRepository()->getIp().':8006/ogrepository/v1/images/rename', $params); if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR ) { $conditional = true; - break; } } @@ -72,8 +79,6 @@ class RenameAction extends AbstractOgRepositoryController return new JsonResponse(data: ['error' => 'Error renaming image'], status: Response::HTTP_INTERNAL_SERVER_ERROR); } - $image->setName($image->getName().'_bkp'); - $this->entityManager->persist($image); $this->entityManager->flush(); return new JsonResponse(data: [], status: Response::HTTP_OK); diff --git a/src/Dto/Input/ImageInput.php b/src/Dto/Input/ImageInput.php index abb8e01..00dd01d 100644 --- a/src/Dto/Input/ImageInput.php +++ b/src/Dto/Input/ImageInput.php @@ -21,6 +21,7 @@ use Symfony\Component\Validator\Constraints as Assert; final class ImageInput { + #[Assert\NotBlank(message: 'validators.image.name.not_blank')] #[Groups(['image:write'])] #[ApiProperty(description: 'The name of the image', example: "Image 1")] public ?string $name = null; @@ -64,6 +65,10 @@ final class ImageInput #[ApiProperty(description: 'The parent of the image')] public ?self $parent = null; + #[Groups(['image:write'])] + #[ApiProperty(description: 'The parent of the image')] + public ?int $version = null; + #[Groups(['image:write'])] #[ApiProperty(description: 'The remote pc of the image')] public ?bool $remotePc = false; @@ -83,6 +88,7 @@ final class ImageInput $this->comments = $image->getComments(); $this->remotePc = $image->isRemotePc(); $this->isGlobal = $image->isGlobal(); + $this->version = $image->getVersion(); if ($image->getSoftwareProfile()) { $this->softwareProfile = new SoftwareProfileOutput($image->getSoftwareProfile()); diff --git a/src/Dto/Output/ImageImageRepositoryOutput.php b/src/Dto/Output/ImageImageRepositoryOutput.php index 665b565..2db73bc 100644 --- a/src/Dto/Output/ImageImageRepositoryOutput.php +++ b/src/Dto/Output/ImageImageRepositoryOutput.php @@ -24,6 +24,9 @@ class ImageImageRepositoryOutput extends AbstractOutput #[Groups(['image-image-repository:read', 'image:read'])] public ?string $datasize = null; + #[Groups(['image:read', 'image-image-repository:read'])] + public ?int $version = null; + #[Groups(['image-image-repository:read', 'image:read'])] public \DateTime $createdAt; @@ -44,6 +47,7 @@ class ImageImageRepositoryOutput extends AbstractOutput $this->imageRepository = new ImageRepositoryOutput($imageImageRepository->getRepository()); } + $this->version = $imageImageRepository->getVersion(); $this->status = $imageImageRepository->getStatus(); $this->imageFullsum = $imageImageRepository->getImageFullsum(); $this->datasize = $imageImageRepository->getDatasize(); diff --git a/src/Dto/Output/ImageOutput.php b/src/Dto/Output/ImageOutput.php index d1f4aa1..5daead3 100644 --- a/src/Dto/Output/ImageOutput.php +++ b/src/Dto/Output/ImageOutput.php @@ -39,6 +39,9 @@ final class ImageOutput extends AbstractOutput #[Groups(['image:read'])] public ?array $partitionInfo = null; + #[Groups(['image:read', 'image-image-repository:read'])] + public ?int $version = null; + #[Groups(['image:read'])] public \DateTime $createdAt; @@ -58,6 +61,7 @@ final class ImageOutput extends AbstractOutput fn(ImageImageRepository $image) => new ImageImageRepositoryOutput($image) )->toArray(); + $this->version = $image->getVersion(); $this->partitionInfo = json_decode($image->getPartitionInfo(), true); $this->remotePc = $image->isRemotePc(); $this->isGlobal = $image->isGlobal(); diff --git a/src/Entity/Image.php b/src/Entity/Image.php index e9607d7..727c212 100644 --- a/src/Entity/Image.php +++ b/src/Entity/Image.php @@ -46,6 +46,9 @@ class Image extends AbstractEntity #[ORM\OneToMany(mappedBy: 'image', targetEntity: ImageImageRepository::class, cascade: ['persist'], orphanRemoval: true)] private Collection $imageImageRepositories; + #[ORM\Column(nullable: true)] + private ?int $version = null; + public function __construct() { parent::__construct(); @@ -191,4 +194,16 @@ class Image extends AbstractEntity return false; } + + public function getVersion(): ?int + { + return $this->version; + } + + public function setVersion(?int $version): static + { + $this->version = $version; + + return $this; + } } diff --git a/src/Entity/ImageImageRepository.php b/src/Entity/ImageImageRepository.php index a215c5c..d80012c 100644 --- a/src/Entity/ImageImageRepository.php +++ b/src/Entity/ImageImageRepository.php @@ -7,8 +7,6 @@ use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; #[ORM\Entity(repositoryClass: ImageImageRepositoryRepository::class)] -#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_IMAGE_REPOSITORY', columns: ['image_id', 'repository_id'])] -#[UniqueEntity(fields: ['image', 'repository'], message: 'This image is already associated with this repository')] class ImageImageRepository extends AbstractEntity { #[ORM\ManyToOne(targetEntity: Image::class, cascade: ['persist'], inversedBy: 'imageImageRepositories')] @@ -31,6 +29,9 @@ class ImageImageRepository extends AbstractEntity #[ORM\Column(length: 255, nullable: true)] private ?string $datasize = null; + #[ORM\Column(nullable: true)] + private ?int $version = null; + public function getImage(): ?Image { return $this->image; @@ -102,4 +103,16 @@ class ImageImageRepository extends AbstractEntity return $this; } + + public function getVersion(): ?int + { + return $this->version; + } + + public function setVersion(?int $version): static + { + $this->version = $version; + + return $this; + } } diff --git a/src/Model/CommandTypes.php b/src/Model/CommandTypes.php index 40c65af..4f31c21 100644 --- a/src/Model/CommandTypes.php +++ b/src/Model/CommandTypes.php @@ -12,6 +12,7 @@ final class CommandTypes public const string BACKUP_IMAGE = 'backup-image'; public const string IMPORT_IMAGE = 'import-image'; public const string EXPORT_IMAGE = 'export-image'; + public const string RENAME_IMAGE = 'rename-image'; public const string CONVERT_IMAGE_TO_VIRTUAL = 'convert-image-to-virtual'; public const string TRANSFER_IMAGE = 'transfer-image'; public const string POWER_ON = 'power-on'; @@ -33,6 +34,7 @@ final class CommandTypes self::BACKUP_IMAGE => 'Backup Image', self::IMPORT_IMAGE => 'Import image', self::EXPORT_IMAGE => 'Export image', + self::RENAME_IMAGE => 'Rename Image', self::POWER_ON => 'Encender', self::REBOOT => 'Reiniciar', self::SHUTDOWN => 'Apagar', diff --git a/src/Repository/ImageImageRepositoryRepository.php b/src/Repository/ImageImageRepositoryRepository.php index a72dc6b..65c57f4 100644 --- a/src/Repository/ImageImageRepositoryRepository.php +++ b/src/Repository/ImageImageRepositoryRepository.php @@ -15,4 +15,14 @@ class ImageImageRepositoryRepository extends AbstractRepository { parent::__construct($registry, ImageImageRepository::class); } + + public function findLatestVersion(): ?ImageImageRepository + { + return $this->createQueryBuilder('i') + ->orderBy('i.version', 'DESC') + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + } + } diff --git a/src/State/Processor/ImageProcessor.php b/src/State/Processor/ImageProcessor.php index 80a9b4d..d4e2f18 100644 --- a/src/State/Processor/ImageProcessor.php +++ b/src/State/Processor/ImageProcessor.php @@ -63,21 +63,23 @@ readonly class ImageProcessor implements ProcessorInterface $entity = $this->imageRepository->findOneByUuid($uriVariables['uuid']); } - $image = $data->createOrUpdateEntity($entity); - $this->validator->validate($image); if ($data->selectedImage){ - $content = $this->renameActionController->__invoke($image); + $content = $this->renameActionController->__invoke($data->selectedImage->getEntity()); if ($content->getStatusCode() !== 200){ throw new \Exception('Error renaming image'); } + $response = $this->createImageActionController->__invoke($data->selectedImage->getEntity()); + + } else { + $image = $data->createOrUpdateEntity($entity); + + $response = $this->createImageActionController->__invoke($image); + $this->imageRepository->save($image); } - $response = $this->createImageActionController->__invoke($image); - $this->imageRepository->save($image); - - return new ImageOutput($image); + return new ImageOutput($data->selectedImage?->getEntity() ?? $image); } private function processDelete($data, Operation $operation, array $uriVariables = [], array $context = []): null