Files
keystone/app/Jobs/Services/DeployService.php

105 lines
3.2 KiB
PHP

<?php
namespace App\Jobs\Services;
use App\Actions\Services\ResolveServiceImageDigest;
use App\Data\Operations\PlannedStep;
use App\Enums\OperationKind;
use App\Enums\OperationStatus;
use App\Enums\ServiceStatus;
use App\Models\Operation;
use App\Models\Service;
use App\Services\Compose\ComposeRenderer;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use InvalidArgumentException;
class DeployService implements ShouldQueue
{
use Queueable;
protected Operation $operation;
public function __construct(
public Service $service,
) {
//
}
public function handle(): void
{
$driver = $this->service->driver();
$this->service->forceFill([
'available_image_digest' => app(ResolveServiceImageDigest::class)->execute($this->service),
])->save();
$this->service->update([
'status' => ServiceStatus::INSTALLING,
]);
$this->operation = $this->service->operations()->create([
'kind' => OperationKind::SERVICE_DEPLOY,
'status' => OperationStatus::PENDING,
]);
$operationPlan = $driver->getOperationPlan($this->operation->hash);
$steps = $this->stepsWithComposeUpload($operationPlan->steps);
foreach ($steps as $index => $plannedStep) {
$step = $this->operation->steps()->create([
'name' => $plannedStep->name,
'order' => $index + 1,
'status' => OperationStatus::PENDING,
'script' => $plannedStep->getScriptTemplate(),
'secrets' => $plannedStep->secrets(),
]);
if ($index === 0) {
$step->dispatchJob();
}
}
}
/**
* @param array<int, \App\Data\Operations\PlannedStep> $steps
* @return array<int, \App\Data\Operations\PlannedStep>
*/
private function stepsWithComposeUpload(array $steps): array
{
try {
$renderer = app(ComposeRenderer::class);
$compose = $renderer->render($this->service);
$env = $renderer->renderEnvironmentFile($this->service);
} catch (InvalidArgumentException) {
return $steps;
}
return [
new PlannedStep(
name: 'Upload Compose file',
script: $this->composeUploadScript($compose, $env),
),
...$steps,
];
}
private function composeUploadScript(string $compose, string $env): string
{
$servicePath = "/home/keystone/services/{$this->service->id}";
return implode("\n", [
"mkdir -p {$servicePath}",
'printf %s '.escapeshellarg(base64_encode($compose))." | base64 -d > {$servicePath}/compose.yml",
'printf %s '.escapeshellarg(base64_encode($env))." | base64 -d > {$servicePath}/.env",
]);
}
public function failed(\Throwable $exception): void
{
if (isset($this->operation)) {
$this->operation->update([
'status' => OperationStatus::FAILED,
]);
$this->service->update([
'status' => ServiceStatus::ERROR,
]);
}
}
}