Add managed registry provisioning, pruning, and readiness tracking
This commit is contained in:
@@ -15,9 +15,13 @@ use App\Enums\ServiceEndpointScope;
|
||||
use App\Models\Environment;
|
||||
use App\Models\EnvironmentAttachment;
|
||||
use App\Models\Operation;
|
||||
use App\Models\Registry;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceReplica;
|
||||
use App\Services\Compose\ComposeRenderer;
|
||||
use App\Services\Registries\RegistryDockerAuthScript;
|
||||
use App\Services\Registries\RegistryResolver;
|
||||
use App\Support\CaddyRouteRenderer;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
@@ -67,6 +71,7 @@ class DeployEnvironment implements ShouldQueue
|
||||
|
||||
$artifact = app(PlanBuildArtifact::class)->execute($this->environment, $commitSha);
|
||||
$artifact = app(BuildApplicationArtifact::class)->execute($artifact, $operation);
|
||||
$registry = app(RegistryResolver::class)->buildRegistryFor($this->environment->application->organisation);
|
||||
|
||||
foreach ($services as $service) {
|
||||
$service->update([
|
||||
@@ -80,8 +85,8 @@ class DeployEnvironment implements ShouldQueue
|
||||
'status' => OperationStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->createServiceDeploySteps($child, $service, $commitSha, $artifact->image_digest);
|
||||
$this->createReplicaDeployOperations($child, $service, $artifact->registry_ref);
|
||||
$this->createServiceDeploySteps($child, $service, $commitSha, $artifact->image_digest, $artifact->registry_ref);
|
||||
$this->createReplicaDeployOperations($child, $service, $artifact->registry_ref, $registry);
|
||||
}
|
||||
|
||||
$this->createGatewayOperations($operation);
|
||||
@@ -100,19 +105,20 @@ class DeployEnvironment implements ShouldQueue
|
||||
->all();
|
||||
}
|
||||
|
||||
private function createServiceDeploySteps(Operation $operation, Service $service, string $commitSha, string $imageDigest): void
|
||||
private function createServiceDeploySteps(Operation $operation, Service $service, string $commitSha, string $imageDigest, ?string $imageReference = null): void
|
||||
{
|
||||
foreach ($this->serviceDeployScripts($service, $commitSha, $imageDigest) as $index => $step) {
|
||||
foreach ($this->serviceDeployScripts($service, $commitSha, $imageDigest, $imageReference) as $index => $step) {
|
||||
$operation->steps()->create([
|
||||
'name' => $step['name'],
|
||||
'order' => $index + 1,
|
||||
'status' => OperationStatus::PENDING,
|
||||
'script' => $step['script'],
|
||||
'secrets' => $step['secrets'] ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function createReplicaDeployOperations(Operation $parent, Service $service, ?string $imageReference = null): void
|
||||
private function createReplicaDeployOperations(Operation $parent, Service $service, ?string $imageReference = null, ?Registry $registry = null): void
|
||||
{
|
||||
$replicas = $this->ensureServiceReplicas($service);
|
||||
|
||||
@@ -133,12 +139,13 @@ class DeployEnvironment implements ShouldQueue
|
||||
'health_status' => 'unknown',
|
||||
]);
|
||||
|
||||
foreach ($this->replicaDeployScripts($service, $replica, $imageReference) as $index => $step) {
|
||||
foreach ($this->replicaDeployScripts($service, $replica, $imageReference, $registry, $serviceReplica) as $index => $step) {
|
||||
$operation->steps()->create([
|
||||
'name' => $step['name'],
|
||||
'order' => $index + 1,
|
||||
'status' => OperationStatus::PENDING,
|
||||
'script' => $step['script'],
|
||||
'secrets' => $step['secrets'] ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -244,7 +251,7 @@ class DeployEnvironment implements ShouldQueue
|
||||
/**
|
||||
* @return array<int, array{name: string, script: string}>
|
||||
*/
|
||||
private function serviceDeployScripts(Service $service, string $commitSha, string $imageDigest): array
|
||||
private function serviceDeployScripts(Service $service, string $commitSha, string $imageDigest, ?string $imageReference = null): array
|
||||
{
|
||||
$servicePath = $this->servicePath($service);
|
||||
$composePath = "{$servicePath}/compose.yml";
|
||||
@@ -264,7 +271,7 @@ class DeployEnvironment implements ShouldQueue
|
||||
],
|
||||
[
|
||||
'name' => 'Render Compose files',
|
||||
'script' => $this->composeUploadScript($service),
|
||||
'script' => $this->composeUploadScript($service, $this->fullImageReference($imageReference, $imageDigest)),
|
||||
],
|
||||
];
|
||||
|
||||
@@ -311,17 +318,32 @@ class DeployEnvironment implements ShouldQueue
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{name: string, script: string}>
|
||||
* @return array<int, array{name: string, script: string, secrets?: array<string, string>}>
|
||||
*/
|
||||
private function replicaDeployScripts(Service $service, int $replica, ?string $imageReference = null): array
|
||||
private function replicaDeployScripts(Service $service, int $replica, ?string $imageReference = null, ?Registry $registry = null, ?ServiceReplica $serviceReplica = null): array
|
||||
{
|
||||
$composePath = $this->servicePath($service).'/compose.yml';
|
||||
$project = "keystone_service_{$service->id}_replica_{$replica}";
|
||||
$serviceKey = $this->serviceKey($service);
|
||||
$targetServer = $serviceReplica?->server ?: $service->server;
|
||||
|
||||
$steps = [];
|
||||
$steps = [
|
||||
[
|
||||
'name' => "Render replica {$replica} Compose files",
|
||||
'script' => $this->composeUploadScript($service, $this->fullImageReference($imageReference, $service->available_image_digest)),
|
||||
],
|
||||
];
|
||||
|
||||
if ($imageReference && $service->available_image_digest) {
|
||||
if ($registry instanceof Registry && $registry->credentials) {
|
||||
$auth = app(RegistryDockerAuthScript::class)->forRuntime($registry, $this->dockerAuthUser($targetServer));
|
||||
$steps[] = [
|
||||
'name' => "Configure registry auth for replica {$replica}",
|
||||
'script' => $auth['script'],
|
||||
'secrets' => $auth['secrets'],
|
||||
];
|
||||
}
|
||||
|
||||
$steps[] = [
|
||||
'name' => "Pull image for replica {$replica}",
|
||||
'script' => 'docker pull '.escapeshellarg($imageReference.'@'.$service->available_image_digest),
|
||||
@@ -354,16 +376,21 @@ class DeployEnvironment implements ShouldQueue
|
||||
];
|
||||
}
|
||||
|
||||
private function composeUploadScript(Service $service): string
|
||||
private function dockerAuthUser(?Server $server): string
|
||||
{
|
||||
return 'root';
|
||||
}
|
||||
|
||||
private function composeUploadScript(Service $service, ?string $fullImageReference = null): string
|
||||
{
|
||||
$servicePath = $this->servicePath($service);
|
||||
|
||||
try {
|
||||
$renderer = app(ComposeRenderer::class);
|
||||
$compose = $renderer->render($service);
|
||||
$compose = $renderer->render($this->serviceForCompose($service, $fullImageReference));
|
||||
$env = $renderer->renderEnvironmentFile($service);
|
||||
} catch (InvalidArgumentException) {
|
||||
$compose = "services:\n {$this->serviceKey($service)}:\n image: \"{$service->available_image_digest}\"\n";
|
||||
$compose = "services:\n {$this->serviceKey($service)}:\n image: \"".($fullImageReference ?: $service->available_image_digest)."\"\n";
|
||||
$env = '';
|
||||
}
|
||||
|
||||
@@ -374,6 +401,27 @@ class DeployEnvironment implements ShouldQueue
|
||||
]);
|
||||
}
|
||||
|
||||
private function fullImageReference(?string $imageReference, ?string $imageDigest): ?string
|
||||
{
|
||||
if (! $imageReference || ! $imageDigest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $imageReference.'@'.$imageDigest;
|
||||
}
|
||||
|
||||
private function serviceForCompose(Service $service, ?string $fullImageReference): Service
|
||||
{
|
||||
if (! $fullImageReference) {
|
||||
return $service;
|
||||
}
|
||||
|
||||
$clone = clone $service;
|
||||
$clone->available_image_digest = $fullImageReference;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{name: string, script: string}>
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user