105 lines
3.2 KiB
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,
|
|
]);
|
|
}
|
|
}
|
|
}
|