81 lines
3.0 KiB
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;
|
|
}
|
|
}
|