logs ot fixes in executeNow.

errors handled properly, etc.
This commit is contained in:
Andras Bacsai 2023-04-14 21:09:38 +02:00
parent 14919980c2
commit 46a543441f
9 changed files with 157 additions and 68 deletions

3
.gitignore vendored
View File

@ -20,3 +20,6 @@ yarn-error.log
/.npm /.npm
/.bash_history /.bash_history
/_volumes /_volumes
.lesshst
psysh_history

View File

@ -16,7 +16,9 @@ class RunRemoteProcess
public bool $hideFromOutput; public bool $hideFromOutput;
public bool $setStatus; public bool $isFinished;
public bool $ignoreErrors;
protected $timeStart; protected $timeStart;
@ -31,7 +33,7 @@ class RunRemoteProcess
/** /**
* Create a new job instance. * Create a new job instance.
*/ */
public function __construct(Activity $activity, bool $hideFromOutput = false, bool $setStatus = false) public function __construct(Activity $activity, bool $hideFromOutput = false, bool $isFinished = false, bool $ignoreErrors = false)
{ {
if ($activity->getExtraProperty('type') !== ActivityTypes::REMOTE_PROCESS->value && $activity->getExtraProperty('type') !== ActivityTypes::DEPLOYMENT->value) { if ($activity->getExtraProperty('type') !== ActivityTypes::REMOTE_PROCESS->value && $activity->getExtraProperty('type') !== ActivityTypes::DEPLOYMENT->value) {
@ -40,36 +42,41 @@ class RunRemoteProcess
$this->activity = $activity; $this->activity = $activity;
$this->hideFromOutput = $hideFromOutput; $this->hideFromOutput = $hideFromOutput;
$this->setStatus = $setStatus; $this->isFinished = $isFinished;
$this->ignoreErrors = $ignoreErrors;
} }
public function __invoke(): ProcessResult public function __invoke(): ProcessResult
{ {
$this->activity->properties = $this->activity->properties->merge([
'status' => ProcessStatus::IN_PROGRESS,
]);
$this->timeStart = hrtime(true); $this->timeStart = hrtime(true);
$status = ProcessStatus::IN_PROGRESS;
$processResult = Process::run($this->getCommand(), $this->handleOutput(...)); $processResult = Process::run($this->getCommand(), $this->handleOutput(...));
$status = $processResult->exitCode() != 0 ? ProcessStatus::ERROR : ($this->setStatus ? ProcessStatus::FINISHED : null); if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
$status = ProcessStatus::ERROR;
} else {
if (($processResult->exitCode() == 0 && $this->isFinished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
$status = ProcessStatus::FINISHED;
}
if ($processResult->exitCode() != 0 && !$this->ignoreErrors) {
$status = ProcessStatus::ERROR;
}
}
$this->activity->properties = $this->activity->properties->merge([ $this->activity->properties = $this->activity->properties->merge([
'exitCode' => $processResult->exitCode(), 'exitCode' => $processResult->exitCode(),
'stdout' => $this->hideFromOutput || $processResult->output(), 'stdout' => $processResult->output(),
'stderr' => $processResult->errorOutput(), 'stderr' => $processResult->errorOutput(),
]);
if (isset($status)) {
$this->activity->properties = $this->activity->properties->merge([
'status' => $status->value, 'status' => $status->value,
]); ]);
}
$this->activity->save(); $this->activity->save();
if ($processResult->exitCode() != 0 && $processResult->errorOutput()) { if ($processResult->exitCode() != 0 && !$this->ignoreErrors) {
throw new \RuntimeException('Remote command failed'); throw new \RuntimeException($processResult->errorOutput());
} }
return $processResult; return $processResult;
} }
@ -98,9 +105,7 @@ class RunRemoteProcess
if ($this->hideFromOutput) { if ($this->hideFromOutput) {
return; return;
} }
$this->currentTime = $this->elapsedTime(); $this->currentTime = $this->elapsedTime();
$this->activity->description = $this->encodeOutput($type, $output); $this->activity->description = $this->encodeOutput($type, $output);
if ($this->isAfterLastThrottle()) { if ($this->isAfterLastThrottle()) {

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Livewire;
use App\Models\Application;
use Livewire\Component;
class ApplicationForm extends Component
{
protected Application $application;
public string $applicationId;
public string $name;
public string|null $fqdn;
public string $git_repository;
public string $git_branch;
public string|null $git_commit_sha;
public function mount() {
$this->application = Application::find($this->applicationId);
$this->fill([
'name' => $this->application->name,
'fqdn' => $this->application->fqdn,
'git_repository' => $this->application->git_repository,
'git_branch' => $this->application->git_branch,
'git_commit_sha' => $this->application->git_commit_sha,
]);
}
}

View File

@ -9,7 +9,7 @@ use Visus\Cuid2\Cuid2;
class DeployApplication extends Component class DeployApplication extends Component
{ {
public string $application_uuid; public string $applicationId;
public $activity; public $activity;
public $status; public $status;
public Application $application; public Application $application;
@ -19,10 +19,9 @@ class DeployApplication extends Component
protected array $command = []; protected array $command = [];
protected $source; protected $source;
public function mount($application_uuid) public function mount($applicationId)
{ {
$this->application_uuid = $application_uuid; $this->application = Application::find($applicationId)->first();
$this->application = Application::where('uuid', $this->application_uuid)->first();
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first(); $this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
} }
@ -39,7 +38,7 @@ class DeployApplication extends Component
dispatch(new DeployApplicationJob( dispatch(new DeployApplicationJob(
deployment_uuid: $this->deployment_uuid, deployment_uuid: $this->deployment_uuid,
application_uuid: $this->application_uuid, application_uuid: $this->application->uuid,
)); ));
$currentUrl = url()->previous(); $currentUrl = url()->previous();
@ -49,13 +48,13 @@ class DeployApplication extends Component
public function stop() public function stop()
{ {
runRemoteCommandSync($this->destination->server, ["docker stop -t 0 {$this->application_uuid} >/dev/null 2>&1"]); runRemoteCommandSync($this->destination->server, ["docker stop -t 0 {$this->application->uuid} >/dev/null 2>&1"]);
$this->application->status = 'stopped'; $this->application->status = 'stopped';
$this->application->save(); $this->application->save();
} }
public function kill() public function kill()
{ {
runRemoteCommandSync($this->destination->server, ["docker rm -f {$this->application_uuid}"]); runRemoteCommandSync($this->destination->server, ["docker rm -f {$this->application->uuid}"]);
if ($this->application->status != 'exited') { if ($this->application->status != 'exited') {
$this->application->status = 'exited'; $this->application->status = 'exited';
$this->application->save(); $this->application->save();

View File

@ -75,6 +75,7 @@ class DeployApplicationJob implements ShouldQueue
*/ */
public function handle(): void public function handle(): void
{ {
try {
$coolify_instance_settings = CoolifyInstanceSettings::find(1); $coolify_instance_settings = CoolifyInstanceSettings::find(1);
$this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first(); $this->source = $this->application->source->getMorphClass()::where('id', $this->application->source->id)->first();
@ -90,23 +91,23 @@ class DeployApplicationJob implements ShouldQueue
} }
$this->workdir = "/artifacts/{$this->deployment_uuid}"; $this->workdir = "/artifacts/{$this->deployment_uuid}";
$this->executeNow([ // $this->executeNow([
"docker inspect {$this->application->uuid} >/dev/null 2>&1", // "docker inspect {$this->application->uuid} >/dev/null 2>&1",
"echo $?" // "echo $?"
], 'stopped_container_check', hideFromOutput: true); // ], 'stopped_container_check', hideFromOutput: true, ignoreErrors: true);
if ($this->activity->properties->get('stopped_container_check') == 0) { // if ($this->activity->properties->get('stopped_container_check') == 0) {
$this->executeNow([ // $this->executeNow([
"echo -n 'Container {$this->application->uuid} was stopped, starting it...'" // "echo 'Application is already available. Starting it...'"
]); // ]);
$this->executeNow([ // $this->executeNow([
"docker start {$this->application->uuid}" // "docker start {$this->application->uuid}"
], hideFromOutput: true); // ], hideFromOutput: true);
$this->executeNow([ // $this->executeNow([
"echo 'Started! 🎉'" // "echo 'Done. 🎉'",
], setStatus: true); // ], isFinished: true);
} else { // } else {
// Pull builder image // Pull builder image
$this->executeNow([ $this->executeNow([
"echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'", "echo 'Starting deployment of {$this->application->git_repository}:{$this->application->git_branch}...'",
@ -132,6 +133,26 @@ class DeployApplicationJob implements ShouldQueue
$this->executeNow([$this->execute_in_builder("cd {$this->workdir} && git rev-parse HEAD")], 'commit_sha', hideFromOutput: true); $this->executeNow([$this->execute_in_builder("cd {$this->workdir} && git rev-parse HEAD")], 'commit_sha', hideFromOutput: true);
$this->git_commit = $this->activity->properties->get('commit_sha'); $this->git_commit = $this->activity->properties->get('commit_sha');
$this->executeNow([
"docker inspect {$this->application->uuid} --format '{{json .Config.Image}}' 2>&1",
], 'stopped_container_image', hideFromOutput: true, ignoreErrors: true);
$image = $this->activity->properties->get('stopped_container_image');
if (isset($image)) {
$image = explode(':', str_replace('"', '', $image))[1];
if ($image == $this->git_commit) {
$this->executeNow([
"echo 'Application found locally with the same Git Commit SHA. Starting it...'"
]);
$this->executeNow([
"docker start {$this->application->uuid}"
], hideFromOutput: true);
$this->executeNow([
"echo 'Done. 🎉'",
], isFinished: true);
return;
}
}
$this->executeNow([ $this->executeNow([
$this->execute_in_builder("rm -fr {$this->workdir}/.git") $this->execute_in_builder("rm -fr {$this->workdir}/.git")
], hideFromOutput: true); ], hideFromOutput: true);
@ -173,14 +194,21 @@ class DeployApplicationJob implements ShouldQueue
$this->executeNow([ $this->executeNow([
"echo 'Done. 🎉'", "echo 'Done. 🎉'",
"docker stop -t 0 {$this->deployment_uuid} >/dev/null" ], isFinished: true);
], setStatus: true);
}
dispatch(new ContainerStatusJob($this->application_uuid));
// Saving docker-compose.yml // Saving docker-compose.yml
Storage::disk('deployments')->put(Str::kebab($this->application->name) . '/docker-compose.yml', $docker_compose); Storage::disk('deployments')->put(Str::kebab($this->application->name) . '/docker-compose.yml', $docker_compose);
// }
} catch (\Exception $e) {
$this->executeNow([
"echo 'Oops something is not okay, are you okay? 😢'",
"echo '\n\n{$e->getMessage()}'",
]);
throw new \Exception('Deployment finished');
} finally {
$this->executeNow(["docker rm -f {$this->deployment_uuid} >/dev/null 2>&1"], hideFromOutput: true);
dispatch(new ContainerStatusJob($this->application_uuid));
}
} }
private function execute_in_builder(string $command) private function execute_in_builder(string $command)
@ -314,8 +342,14 @@ class DeployApplicationJob implements ShouldQueue
return $labels; return $labels;
} }
private function executeNow(array|Collection $command, string $propertyName = null, bool $hideFromOutput = false, $setStatus = false, bool $isDebuggable = false) private function executeNow(
{ array|Collection $command,
string $propertyName = null,
bool $isFinished = false,
bool $hideFromOutput = false,
bool $isDebuggable = false,
bool $ignoreErrors = false
) {
static::$batch_counter++; static::$batch_counter++;
if ($command instanceof Collection) { if ($command instanceof Collection) {
@ -334,16 +368,20 @@ class DeployApplicationJob implements ShouldQueue
$remoteProcess = resolve(RunRemoteProcess::class, [ $remoteProcess = resolve(RunRemoteProcess::class, [
'activity' => $this->activity, 'activity' => $this->activity,
'hideFromOutput' => $hideFromOutput, 'hideFromOutput' => $hideFromOutput,
'setStatus' => $setStatus, 'isFinished' => $isFinished,
'ignoreErrors' => $ignoreErrors,
]); ]);
$result = $remoteProcess(); $result = $remoteProcess();
if ($propertyName) { if ($propertyName) {
$this->activity->properties = $this->activity->properties->merge([ $this->activity->properties = $this->activity->properties->merge([
$propertyName => trim($result->output()), $propertyName => trim($result->output()),
]); ]);
$this->activity->save(); $this->activity->save();
} }
if ($result->exitCode() != 0 && $result->errorOutput() && !$ignoreErrors) {
throw new \RuntimeException($result->errorOutput());
}
} }
private function gitImport() private function gitImport()
{ {

View File

@ -1,6 +1,6 @@
FROM serversideup/php:8.2-fpm-nginx FROM serversideup/php:8.2-fpm-nginx
ARG POSTGRES_VERSION=15 ARG POSTGRES_VERSION=15
RUN apt-get update && apt-get install -y php-pgsql openssh-client git git-lfs RUN apt-get update && apt-get install -y php-pgsql openssh-client git git-lfs postgresql-client
RUN apt-get -y autoremove \ RUN apt-get -y autoremove \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*

View File

@ -1,3 +1,3 @@
/* @tailwind base; /* @tailwind base; */
@tailwind components; @tailwind components;
@tailwind utilities; */ @tailwind utilities;

View File

@ -0,0 +1,15 @@
<div>
<form class="flex flex-col">
<label>Name</label>
<input wire:model="name" type="text" name="name" />
<label>Fqdn</label>
<input wire:model="fqdn" type="text" name="fqdn" />
<label>Repository</label>
<input wire:model="git_repository" type="text" name="git_repository" />
<label>Branch</label>
<input wire:model="git_branch" type="text" name="git_branch" />
<label>Commit SHA</label>
<input wire:model="git_commit_sha" type="text" name="git_commit_sha" />
</form>
</div>

View File

@ -1,7 +1,7 @@
<x-layout> <x-layout>
<h1>Application</h1> <h1>Application</h1>
<p>Name: {{ $application->name }}</p> <livewire:deploy-application :applicationId="$application->id" />
<livewire:deploy-application :application_uuid="$application->uuid" /> <livewire:application-form :applicationId="$application->id" />
<div> <div>
<h2>Deployments</h2> <h2>Deployments</h2>
@foreach ($deployments as $deployment) @foreach ($deployments as $deployment)