oh wow, it is cool!

This commit is contained in:
Andras Bacsai 2023-09-14 15:52:04 +02:00
parent 53c20e1e99
commit 8412802f4d
11 changed files with 194 additions and 24 deletions

View File

@ -2,10 +2,8 @@
namespace App\Http\Livewire\Project\Application;
use App\Jobs\ApplicationContainerStatusJob;
use App\Jobs\ContainerStatusJob;
use App\Models\Application;
use App\Notifications\Application\StatusChanged;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
@ -23,9 +21,11 @@ class Heading extends Component
public function check_status()
{
ray($this->application->destination->server);
dispatch_sync(new ContainerStatusJob($this->application->destination->server));
$this->application->refresh();
$this->application->previews->each(function ($preview) {
$preview->refresh();
});
}
public function force_deploy_without_cache()

View File

@ -3,6 +3,7 @@
namespace App\Http\Livewire\Project\Application;
use App\Jobs\ApplicationContainerStatusJob;
use App\Jobs\ContainerStatusJob;
use App\Models\Application;
use App\Models\ApplicationPreview;
use Illuminate\Support\Collection;
@ -23,14 +24,6 @@ class Previews extends Component
$this->parameters = get_route_parameters();
}
public function loadStatus($pull_request_id)
{
dispatch(new ApplicationContainerStatusJob(
application: $this->application,
pullRequestId: $pull_request_id
));
}
public function load_prs()
{
try {

View File

@ -119,6 +119,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ($containers->count() > 0) {
$this->currently_running_container_name = data_get($containers[0], 'Names');
}
if ($this->pull_request_id !== 0 && $this->pull_request_id !== null) {
$this->currently_running_container_name = $this->container_name;
}
$this->application_deployment_queue->update([
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
]);
@ -296,7 +299,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// $this->generate_build_env_variables();
// $this->add_build_env_variables_to_dockerfile();
$this->build_image();
$this->rolling_update();
$this->stop_running_container();
$this->execute_remote_command(
["echo -n 'Starting preview deployment.'"],
[$this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
);
}
private function prepare_builder_image()
@ -576,10 +583,15 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
private function set_labels_for_applications()
{
$appId = $this->application->id;
if ($this->pull_request_id !== 0) {
$appId = $appId . '-pr-' . $this->pull_request_id;
}
$labels = [];
$labels[] = 'coolify.managed=true';
$labels[] = 'coolify.version=' . config('version');
$labels[] = 'coolify.applicationId=' . $this->application->id;
$labels[] = 'coolify.applicationId=' . $appId;
$labels[] = 'coolify.type=application';
$labels[] = 'coolify.name=' . $this->application->name;
if ($this->pull_request_id !== 0) {

View File

@ -3,10 +3,12 @@
namespace App\Jobs;
use App\Actions\Proxy\StartProxy;
use App\Models\ApplicationPreview;
use App\Models\Server;
use App\Notifications\Container\ContainerRestarted;
use App\Notifications\Container\ContainerStopped;
use App\Notifications\Server\Unreachable;
use Arr;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldBeUnique;
@ -69,6 +71,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypt
$containers = format_docker_command_output_to_json($containers);
$applications = $this->server->applications();
$databases = $this->server->databases();
$previews = $this->server->previews();
if ($this->server->isProxyShouldRun()) {
$foundProxyContainer = $containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-proxy';
@ -78,12 +81,148 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypt
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
}
}
$foundApplications = [];
$foundApplicationPreviews = [];
$foundDatabases = [];
foreach ($containers as $container) {
$containerStatus = data_get($container, 'State.Status');
$labels = data_get($container, 'Config.Labels');
$labels = Arr::undot(format_docker_labels_to_json($labels));
$labelId = data_get($labels, 'coolify.applicationId');
ray($labelId);
if ($labelId) {
if (str_contains($labelId,'-pr-')) {
$previewId = (int) Str::after($labelId, '-pr-');
$applicationId = (int) Str::before($labelId, '-pr-');
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id',$previewId)->first();
if ($preview) {
$foundApplicationPreviews[] = $preview->id;
$statusFromDb = $preview->status;
if ($statusFromDb !== $containerStatus) {
$preview->update(['status' => $containerStatus]);
}
} else {
//Notify user that this container should not be there.
}
} else {
$application = $applications->where('id', $labelId)->first();
if ($application) {
$foundApplications[] = $application->id;
$statusFromDb = $application->status;
if ($statusFromDb !== $containerStatus) {
$application->update(['status' => $containerStatus]);
}
} else {
//Notify user that this container should not be there.
}
}
} else {
$uuid = data_get($labels, 'com.docker.compose.service');
if ($uuid) {
$database = $databases->where('uuid', $uuid)->first();
if ($database) {
$foundDatabases[] = $database->id;
$statusFromDb = $database->status;
if ($statusFromDb !== $containerStatus) {
$database->update(['status' => $containerStatus]);
}
} else {
// Notify user that this container should not be there.
}
}
}
}
$notRunningApplications = $applications->pluck('id')->diff($foundApplications);
foreach($notRunningApplications as $applicationId) {
$application = $applications->where('id', $applicationId)->first();
if ($application->status === 'exited') {
continue;
}
$application->update(['status' => 'exited']);
$name = data_get($application, 'name');
$fqdn = data_get($application, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
$project = data_get($application, 'environment.project');
$environment = data_get($application, 'environment');
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $application->uuid;
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
foreach ($notRunningApplicationPreviews as $previewId) {
$preview = $previews->where('id', $previewId)->first();
if ($preview->status === 'exited') {
continue;
}
$preview->update(['status' => 'exited']);
$name = data_get($preview, 'name');
$fqdn = data_get($preview, 'fqdn');
$containerName = $name ? "$name ($fqdn)" : $fqdn;
$project = data_get($preview, 'application.environment.project');
$environment = data_get($preview, 'application.environment');
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/application/" . $preview->application->uuid;
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
}
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
foreach($notRunningDatabases as $database) {
$database = $databases->where('id', $database)->first();
if ($database->status === 'exited') {
continue;
}
$database->update(['status' => 'exited']);
$name = data_get($database, 'name');
$fqdn = data_get($database, 'fqdn');
$containerName = $name;
$project = data_get($database, 'environment.project');
$environment = data_get($database, 'environment');
$url = base_url() . '/project/' . $project->uuid . "/" . $environment->name . "/database/" . $database->uuid;
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
}
return;
foreach ($applications as $application) {
$uuid = data_get($application, 'uuid');
$foundContainer = $containers->filter(function ($value, $key) use ($uuid) {
return Str::startsWith(data_get($value, 'Name'), "/$uuid");
$id = data_get($application, 'id');
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid) {
$labels = data_get($value, 'Config.Labels');
$labels = Arr::undot(format_docker_labels_to_json($labels));
$labelId = data_get($labels, 'coolify.applicationId');
if ($labelId == $id) {
return $value;
}
$isPR = Str::startsWith(data_get($value, 'Name'), "/$uuid");
$isPR = Str::contains(data_get($value, 'Name'), "-pr-");
if ($isPR) {
ray('is pr');
return false;
}
return $value;
})->first();
ray($foundContainer);
if ($foundContainer) {
$containerStatus = data_get($foundContainer, 'State.Status');
$databaseStatus = data_get($application, 'status');
@ -103,6 +242,19 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeUnique, ShouldBeEncrypt
$this->server->team->notify(new ContainerStopped($containerName, $this->server, $url));
}
}
$previews = $application->previews;
foreach ($previews as $preview) {
$foundContainer = $containers->filter(function ($value, $key) use ($id, $uuid, $preview) {
$labels = data_get($value, 'Config.Labels');
$labels = Arr::undot(format_docker_labels_to_json($labels));
$labelId = data_get($labels, 'coolify.applicationId');
if ($labelId == "$id-pr-{$preview->id}") {
return $value;
}
return Str::startsWith(data_get($value, 'Name'), "/$uuid-pr-{$preview->id}");
})->first();
}
}
foreach ($databases as $database) {
$uuid = data_get($database, 'uuid');

View File

@ -105,6 +105,14 @@ class Server extends BaseModel
})->flatten();
}
public function previews() {
return $this->destinations()->map(function ($standaloneDocker) {
return $standaloneDocker->applications->map(function ($application) {
return $application->previews;
})->flatten();
})->flatten();
}
public function destinations()
{
$standalone_docker = $this->hasMany(StandaloneDocker::class)->get();

View File

@ -26,7 +26,7 @@ class ContainerStopped extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
$mail = new MailMessage();
$mail->subject("⛔ Container ({$this->name}) has been stopped on {$this->server->name}");
$mail->subject("⛔ Container {$this->name} has been stopped on {$this->server->name}");
$mail->view('emails.container-stopped', [
'containerName' => $this->name,
'serverName' => $this->server->name,
@ -37,12 +37,12 @@ class ContainerStopped extends Notification implements ShouldQueue
public function toDiscord(): string
{
$message = "⛔ Container ({$this->name}) has been stopped on {$this->server->name}";
$message = "⛔ Container {$this->name} has been stopped on {$this->server->name}";
return $message;
}
public function toTelegram(): array
{
$message = "⛔ Container ({$this->name}) has been stopped on {$this->server->name}";
$message = "⛔ Container ($this->name} has been stopped on {$this->server->name}";
$payload = [
"message" => $message,
];

View File

@ -91,7 +91,7 @@ function generateApplicationContainerName(string $uuid, int $pull_request_id = 0
{
$now = now()->format('Hisu');
if ($pull_request_id !== 0 && $pull_request_id !== null) {
return $uuid . '-pr-' . $pull_request_id . '-' . $now;
return $uuid . '-pr-' . $pull_request_id;
} else {
return $uuid . '-' . $now;
}

View File

@ -3,9 +3,13 @@
Container ({{ $containerName }}) has been restarted automatically on {{$serverName}}, because it was stopped unexpected.
@if ($containerName === 'coolify-proxy')
Coolify Proxy should run on your server as you have FQDN set up in one of your resources. If you don't want to use Coolify Proxy, please remove FQDN from your resources.
Coolify Proxy should run on your server as you have FQDNs set up in one of your resources.
Note: The proxy should not stop unexpectedly, so please check what is going on your server.
If you don't want to use Coolify Proxy, please remove FQDN from your resources or set Proxy type to Custom(None).
@endif
</x-emails.layout>

View File

@ -48,10 +48,10 @@
@endif
</div>
@if ($application->previews->count() > 0)
<h4 class="py-4" wire:poll.10000ms='previewRefresh'>Deployed Previews</h4>
<h4 class="py-4">Deployed Previews</h4>
<div class="flex gap-6 ">
@foreach ($application->previews as $preview)
<div class="flex flex-col p-4 bg-coolgray-200 " x-init="$wire.loadStatus('{{ data_get($preview, 'pull_request_id') }}')">
<div class="flex flex-col p-4 bg-coolgray-200">
<div class="flex gap-2">PR #{{ data_get($preview, 'pull_request_id') }} |
@if (data_get($preview, 'status') === 'running')
<x-status.running />

View File

@ -116,7 +116,7 @@ Route::post('/source/github/events', function () {
$applications = $applications->where('git_branch', $base_branch)->get();
}
if ($applications->isEmpty()) {
return response('Nothing to do. No applications found.');
return response("Nothing to do. No applications found with branch '$base_branch'.");
}
foreach ($applications as $application) {
$isFunctional = $application->destination->server->isFunctional();
@ -178,6 +178,7 @@ Route::post('/source/github/events', function () {
}
}
} catch (Exception $e) {
ray($e->getMessage());
return general_error_handler(err: $e);
}
});