refs #1857. Rename image API integration
testing/ogcore-api/pipeline/head There was a failure building this commit Details

pull/27/head
Manuel Aranda Rosales 2025-04-10 09:50:59 +02:00
parent 6867f74098
commit 4578f29349
6 changed files with 193 additions and 43 deletions

View File

@ -64,6 +64,14 @@ resources:
uriTemplate: /image-image-repositories/{uuid}/convert-image-to-virtual
controller: App\Controller\OgRepository\Image\ConvertImageToVirtualAction
rename_image_ogrepository:
shortName: OgRepository Server
class: ApiPlatform\Metadata\Post
method: POST
input: App\Dto\Input\RenameImageInput
uriTemplate: /image-image-repositories/{uuid}/rename-image
controller: App\Controller\OgRepository\Image\RenameAction
trash_delete_image_ogrepository:
shortName: OgRepository Server
description: Delete Image in OgRepository

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250409093554 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE image_image_repository ADD name VARCHAR(255) NOT 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 name');
}
}

View File

@ -4,8 +4,10 @@ namespace App\Controller\OgRepository\Image;
use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Dto\Input\DeployImageInput;
use App\Dto\Input\RenameImageInput;
use App\Entity\Image;
use App\Entity\ImageImageRepository;
use App\Entity\ImageRepository;
use App\Model\CommandTypes;
use App\Model\ImageStatus;
use App\Model\TraceStatus;
@ -28,59 +30,96 @@ class RenameAction extends AbstractOgRepositoryController
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
public function __invoke(Image $image): JsonResponse
public function __invoke(RenameImageInput $input, ImageImageRepository $imageImageRepository): JsonResponse
{
$repositories = $image->getImageImageRepositories();
$image = $imageImageRepository->getImage();
if ($repositories->count() === 0) {
return new JsonResponse(data: ['error' => 'Image is not in any repository', 'code' => Response::HTTP_INTERNAL_SERVER_ERROR], status: Response::HTTP_BAD_REQUEST);
}
if ($image->isGlobal()) {
$repositories = $image->getImageImageRepositories();
$allGood = true;
foreach ($repositories as $repository) {
try {
$content = $this->createRequest('GET', 'http://'.$repository->getRepository()->getIp(). ':8006/ogrepository/v1/status');
} catch (TransportExceptionInterface $e) {
$allGood = false;
break;
if ($repositories->count() === 0) {
return $this->jsonError('Image is not in any repository');
}
if (!$this->isAvailableInAllRepositories($repositories)) {
$this->logger->info('Image is not available in all repositories', ['image' => $image->getName()]);
return $this->jsonError('Image is not available in all repositories');
}
$repoWithImage = $this->entityManager
->getRepository(ImageImageRepository::class)
->findBy(['image' => $image, 'repository' => $imageImageRepository->getRepository()]);
} else {
$repoWithImage = [$imageImageRepository];
}
if (!$allGood) {
$this->logger->info('Image is not available in all repositories', ['image' => $image->getName()]);
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;
$latestImageRepo = $this->entityManager->getRepository(ImageImageRepository::class)->findLatestVersion();
$repoWithImage = $this->entityManager->getRepository(ImageImageRepository::class)->findBy(['image' => $image, 'version' => $latestImageRepo->getVersion()]);
$hasError = false;
foreach ($repoWithImage as $repository) {
$params = [
'json' => [
'ID_img' => $repository->getImageFullsum(),
'image_new_name' => $image->getName().'_v'.$image->getVersion(),
]
];
$content = $this->renameImageInRepository($repository, $input->newName);
$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;
if (isset($content['error']) && $content['code'] === Response::HTTP_INTERNAL_SERVER_ERROR) {
$hasError = true;
}
}
if ($conditional) {
return new JsonResponse(data: ['error' => 'Error renaming image'], status: Response::HTTP_INTERNAL_SERVER_ERROR);
$repository->setName($input->newName);
$this->entityManager->persist($repository);
}
$this->entityManager->flush();
return new JsonResponse(data: [], status: Response::HTTP_OK);
if ($hasError) {
return new JsonResponse(['error' => 'Error renaming image'], Response::HTTP_INTERNAL_SERVER_ERROR);
}
return new JsonResponse([], Response::HTTP_OK);
}
/**
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
* @throws ServerExceptionInterface
*/
private function isAvailableInAllRepositories($repositories): bool
{
foreach ($repositories as $repository) {
try {
$this->createRequest('GET', 'http://' . $repository->getRepository()->getIp() . ':8006/ogrepository/v1/status');
} catch (TransportExceptionInterface $e) {
return false;
}
}
return true;
}
/**
* @throws TransportExceptionInterface
* @throws ServerExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
*/
private function renameImageInRepository(ImageImageRepository $repository, string $newName): array
{
$params = [
'json' => [
'ID_img' => $repository->getImageFullsum(),
'image_new_name' => $newName,
]
];
return $this->createRequest(
'PUT',
'http://' . $repository->getRepository()->getIp() . ':8006/ogrepository/v1/images/rename',
$params
);
}
private function jsonError(string $message): JsonResponse
{
return new JsonResponse(
['error' => $message, 'code' => Response::HTTP_INTERNAL_SERVER_ERROR],
Response::HTTP_BAD_REQUEST
);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Dto\Input;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
final class RenameImageInput
{
#[Assert\NotNull]
#[Groups(['image-image-repository:write'])]
public ?string $newName = '';
}

View File

@ -0,0 +1,58 @@
<?php
namespace App\Factory;
use App\Entity\ImageImageRepository;
use App\Repository\ImageImageRepositoryRepository;
use Zenstruck\Foundry\ModelFactory;
use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory;
use Zenstruck\Foundry\Persistence\Proxy;
use Zenstruck\Foundry\Persistence\ProxyRepositoryDecorator;
/**
* @extends PersistentProxyObjectFactory<ImageImageRepository>
*/
final class ImageImageRepositoryFactory extends ModelFactory
{
/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services
*
* @todo inject services if required
*/
public function __construct()
{
parent::__construct();
}
/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories
*
* @todo add your default values here
*/
protected function getDefaults(): array
{
return [
'createdAt' => self::faker()->dateTime(),
'image' => ImageFactory::new(),
'name' => self::faker()->text(255),
'repository' => ImageRepositoryFactory::new(),
'status' => self::faker()->text(255),
'updatedAt' => self::faker()->dateTime(),
];
}
/**
* @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization
*/
protected function initialize(): self
{
return $this
// ->afterInstantiate(function(ImageImageRepository $imageImageRepository): void {})
;
}
protected static function getClass(): string
{
return ImageImageRepository::class;
}
}

View File

@ -9,6 +9,7 @@ use App\Entity\OrganizationalUnit;
use App\Entity\SoftwareProfile;
use App\Factory\ClientFactory;
use App\Factory\ImageFactory;
use App\Factory\ImageImageRepositoryFactory;
use App\Factory\ImageRepositoryFactory;
use App\Factory\OrganizationalUnitFactory;
use App\Factory\SoftwareProfileFactory;
@ -70,12 +71,12 @@ class ImageTest extends AbstractTest
SoftwareProfileFactory::createOne(['description' => self::SOFTWARE_PROFILE]);
$swPIri = $this->findIriBy(SoftwareProfile::class, ['description' => self::SOFTWARE_PROFILE]);
$imageRepositories = ImageRepositoryFactory::createMany(5);
$imageRepositories = ImageImageRepositoryFactory::createMany(5);
$this->createClientWithCredentials()->request('POST', '/images',['json' => [
'name' => self::IMAGE_CREATE,
'softwareProfile' => $swPIri,
'imageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories)
'imageImageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories)
]]);
$this->assertResponseStatusCodeSame(201);
@ -102,11 +103,11 @@ class ImageTest extends AbstractTest
ImageFactory::createOne(['name' => self::IMAGE_CREATE]);
$iri = $this->findIriBy(Image::class, ['name' => self::IMAGE_CREATE]);
$imageRepositories = ImageRepositoryFactory::createMany(5);
$imageRepositories = ImageImageRepositoryFactory::createMany(5);
$this->createClientWithCredentials()->request('PUT', $iri, ['json' => [
'name' => self::IMAGE_UPDATE,
'imageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories)
'imageImageRepositories' => array_map(fn($repo) => '/image-repositories/'. $repo->getUuid(), $imageRepositories)
]]);
$this->assertResponseIsSuccessful();