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 $steps * @return array */ 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, ]); } } }