Files
keystone/app/Services/Registries/ManagedRegistryRetention.php

81 lines
3.0 KiB
PHP

<?php
namespace App\Services\Registries;
use App\Enums\BuildArtifactStatus;
use App\Enums\RegistryType;
use App\Models\BuildArtifact;
use App\Models\Environment;
use App\Models\Registry;
use Illuminate\Support\Collection;
class ManagedRegistryRetention
{
/**
* @return Collection<int, BuildArtifact>
*/
public function markPrunable(Registry $registry): Collection
{
if ($registry->type !== RegistryType::MANAGED) {
return collect();
}
$keep = max(1, (int) $registry->retention_successful_artifacts);
$updated = collect();
Environment::query()
->whereHas('buildArtifacts', fn ($query) => $query
->where('status', BuildArtifactStatus::AVAILABLE)
->where('registry_ref', 'like', rtrim((string) $registry->url, '/').'/%'))
->with(['services', 'buildArtifacts' => fn ($query) => $query
->where('status', BuildArtifactStatus::AVAILABLE)
->where('registry_ref', 'like', rtrim((string) $registry->url, '/').'/%')
->latest()])
->each(function (Environment $environment) use ($keep, $updated): void {
$activeDigests = $environment->services
->flatMap(fn ($service): array => [
$service->available_image_digest,
$service->current_image_digest,
])
->filter()
->all();
$environment->buildArtifacts
->skip($keep)
->filter(fn (BuildArtifact $artifact): bool => ! in_array($artifact->image_digest, $activeDigests, true))
->each(function (BuildArtifact $artifact) use ($updated): void {
$metadata = $artifact->metadata ?? [];
$artifact->update([
'status' => BuildArtifactStatus::PRUNABLE,
'metadata' => [
...$metadata,
'prunable_at' => now()->toIso8601String(),
'prune_command' => $this->deleteManifestCommand($artifact),
],
]);
$updated->push($artifact->refresh());
});
});
return $updated;
}
public function deleteManifestCommand(BuildArtifact $artifact): string
{
$reference = (string) ($artifact->registry_ref ?? '');
$digest = (string) $artifact->image_digest;
return 'curl --fail --silent --show-error --request DELETE '.escapeshellarg('https://'.$this->manifestPath($reference, $digest));
}
private function manifestPath(string $reference, string $digest): string
{
$hostAndPath = preg_replace('/:[^:\/]+$/', '', $reference);
$path = str($hostAndPath)->after('/')->value();
return $path === ''
? 'v2/'
: str($reference)->before('/')->value().'/v2/'.$path.'/manifests/'.$digest;
}
}