Merge pull request #1798 from coollabsio/next

v4.0.0-beta.231
This commit is contained in:
Andras Bacsai 2024-03-02 15:04:48 +01:00 committed by GitHub
commit a7df9fa625
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 180 additions and 102 deletions

View File

@ -727,7 +727,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
$this->write_deployment_configurations(); $this->write_deployment_configurations();
$this->server = $this->original_server; $this->server = $this->original_server;
} }
if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || $this->application->pull_request_id !== 0) { if (count($this->application->ports_mappings_array) > 0 || (bool) $this->application->settings->is_consistent_container_name_enabled || $this->pull_request_id !== 0) {
$this->application_deployment_queue->addLogEntry("----------------------------------------"); $this->application_deployment_queue->addLogEntry("----------------------------------------");
if (count($this->application->ports_mappings_array) > 0) { if (count($this->application->ports_mappings_array) > 0) {
$this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported."); $this->application_deployment_queue->addLogEntry("Application has ports mapped to the host system, rolling update is not supported.");
@ -735,7 +735,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
if ((bool) $this->application->settings->is_consistent_container_name_enabled) { if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
$this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported."); $this->application_deployment_queue->addLogEntry("Consistent container name feature enabled, rolling update is not supported.");
} }
if ($this->application->pull_request_id !== 0) { if ($this->pull_request_id !== 0) {
$this->application->settings->is_consistent_container_name_enabled = true; $this->application->settings->is_consistent_container_name_enabled = true;
$this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported."); $this->application_deployment_queue->addLogEntry("Pull request deployment, rolling update is not supported.");
} }
@ -1215,7 +1215,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
// ]; // ];
// } // }
if ($this->application->pull_request_id === 0) { if ($this->pull_request_id === 0) {
if ((bool)$this->application->settings->is_consistent_container_name_enabled) { if ((bool)$this->application->settings->is_consistent_container_name_enabled) {
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options); $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
if (count($custom_compose) > 0) { if (count($custom_compose) > 0) {

View File

@ -10,29 +10,21 @@ use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Illuminate\Support\Collection;
use Livewire\Component; use Livewire\Component;
class ExecuteContainerCommand extends Component class ExecuteContainerCommand extends Component
{ {
public string $command; public string $command;
public string $container; public string $container;
public $containers; public Collection $containers;
public $parameters; public $parameters;
public $resource; public $resource;
public string $type; public string $type;
public string $workDir = ''; public string $workDir = '';
public Server $server; public Server $server;
public $servers = []; public Collection $servers;
public function getListeners()
{
return [
"serviceStatusChanged",
];
}
public function serviceStatusChanged()
{
$this->getContainers();
}
protected $rules = [ protected $rules = [
'server' => 'required', 'server' => 'required',
'container' => 'required', 'container' => 'required',
@ -43,20 +35,18 @@ class ExecuteContainerCommand extends Component
public function mount() public function mount()
{ {
$this->parameters = get_route_parameters(); $this->parameters = get_route_parameters();
$this->getContainers();
}
public function getContainers()
{
$this->containers = collect(); $this->containers = collect();
$this->servers = collect();
if (data_get($this->parameters, 'application_uuid')) { if (data_get($this->parameters, 'application_uuid')) {
$this->type = 'application'; $this->type = 'application';
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail(); $this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
$this->server = $this->resource->destination->server; if ($this->resource->destination->server->isFunctional()) {
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0); $this->servers = $this->servers->push($this->resource->destination->server);
if ($containers->count() > 0) { }
$containers->each(function ($container) { foreach ($this->resource->additional_servers as $server) {
$this->containers->push(str_replace('/', '', $container['Names'])); if ($server->isFunctional()) {
}); $this->servers = $this->servers->push($server);
}
} }
} else if (data_get($this->parameters, 'database_uuid')) { } else if (data_get($this->parameters, 'database_uuid')) {
$this->type = 'database'; $this->type = 'database';
@ -77,47 +67,85 @@ class ExecuteContainerCommand extends Component
} }
} }
$this->resource = $resource; $this->resource = $resource;
$this->server = $this->resource->destination->server; if ($this->resource->destination->server->isFunctional()) {
$this->servers = $this->servers->push($this->resource->destination->server);
}
$this->container = $this->resource->uuid; $this->container = $this->resource->uuid;
// if (!str(data_get($this,'resource.status'))->startsWith('exited')) { $this->containers->push($this->container);
$this->containers->push($this->container);
// }
} else if (data_get($this->parameters, 'service_uuid')) { } else if (data_get($this->parameters, 'service_uuid')) {
$this->type = 'service'; $this->type = 'service';
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail(); $this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
$this->resource->applications()->get()->each(function ($application) { $this->resource->applications()->get()->each(function ($application) {
// if (str(data_get($application, 'status'))->contains('running')) { $this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
// }
}); });
$this->resource->databases()->get()->each(function ($database) { $this->resource->databases()->get()->each(function ($database) {
// if (str(data_get($database, 'status'))->contains('running')) { $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
// }
}); });
if ($this->resource->server->isFunctional()) {
$this->server = $this->resource->server; $this->servers = $this->servers->push($this->resource->server);
}
} }
if ($this->containers->count() > 0) { if ($this->containers->count() > 0) {
$this->container = $this->containers->first(); $this->container = $this->containers->first();
} }
} }
public function loadContainers()
{
foreach ($this->servers as $server) {
if (data_get($this->parameters, 'application_uuid')) {
if ($server->isSwarm()) {
$containers = collect([
[
'Names' => $this->resource->uuid . '_' . $this->resource->uuid,
]
]);
} else {
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
}
foreach ($containers as $container) {
$payload = [
'server' => $server,
'container' => $container,
];
$this->containers = $this->containers->push($payload);
}
}
}
if ($this->containers->count() > 0) {
if (data_get($this->parameters, 'application_uuid')) {
$this->container = data_get($this->containers->first(), 'container.Names');
} elseif (data_get($this->parameters, 'database_uuid')) {
$this->container = $this->containers->first();
} elseif (data_get($this->parameters, 'service_uuid')) {
$this->container = $this->containers->first();
}
}
}
public function runCommand() public function runCommand()
{ {
$this->validate();
try { try {
if ($this->server->isForceDisabled()) { if (data_get($this->parameters, 'application_uuid')) {
$container = $this->containers->where('container.Names', $this->container)->first();
$container_name = data_get($container, 'container.Names');
if (is_null($container)) {
throw new \RuntimeException('Container not found.');
}
$server = data_get($container, 'server');
} else {
$container_name = $this->container;
$server = $this->servers->first();
}
if ($server->isForceDisabled()) {
throw new \RuntimeException('Server is disabled.'); throw new \RuntimeException('Server is disabled.');
} }
// Wrap command to prevent escaped execution in the host.
$cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command) . '"'; $cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command) . '"';
if (!empty($this->workDir)) { if (!empty($this->workDir)) {
$exec = "docker exec -w {$this->workDir} {$this->container} {$cmd}"; $exec = "docker exec -w {$this->workDir} {$container_name} {$cmd}";
} else { } else {
$exec = "docker exec {$this->container} {$cmd}"; $exec = "docker exec {$container_name} {$cmd}";
} }
$activity = remote_process([$exec], $this->server, ignore_errors: true); $activity = remote_process([$exec], $server, ignore_errors: true);
$this->dispatch('activityMonitor', $activity->id); $this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);

View File

@ -10,50 +10,57 @@ use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql; use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis; use App\Models\StandaloneRedis;
use Illuminate\Support\Collection;
use Livewire\Component; use Livewire\Component;
class Logs extends Component class Logs extends Component
{ {
public ?string $type = null; public ?string $type = null;
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource; public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
public Server $server; public Collection $servers;
public Collection $containers;
public $container = []; public $container = [];
public $containers;
public $parameters; public $parameters;
public $query; public $query;
public $status; public $status;
public $serviceSubType; public $serviceSubType;
public function mount() public function loadContainers($server_id)
{ {
$this->containers = collect(); try {
$this->parameters = get_route_parameters(); $server = $this->servers->firstWhere('id', $server_id);
$this->query = request()->query(); if ($server->isSwarm()) {
if (data_get($this->parameters, 'application_uuid')) {
$this->type = 'application';
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
$this->status = $this->resource->status;
$this->server = $this->resource->destination->server;
if ($this->server->isSwarm()) {
$containers = collect([ $containers = collect([
[ [
'Names' => $this->resource->uuid . '_' . $this->resource->uuid, 'Names' => $this->resource->uuid . '_' . $this->resource->uuid,
] ]
]); ]);
} else { } else {
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, includePullrequests: true); $containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
} }
if ($containers->count() > 0) { $server->containers = $containers;
$containers->each(function ($container) { } catch (\Exception $e) {
$this->containers->push(str_replace('/', '', $container['Names'])); return handleError($e, $this);
}); }
}
public function mount()
{
$this->containers = collect();
$this->servers = collect();
$this->parameters = get_route_parameters();
$this->query = request()->query();
if (data_get($this->parameters, 'application_uuid')) {
$this->type = 'application';
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
$this->status = $this->resource->status;
if ($this->resource->destination->server->isFunctional()) {
$this->servers = $this->servers->push($this->resource->destination->server);
} }
$this->containers = $this->containers->sortByDesc(function ($container) { foreach ($this->resource->additional_servers as $server) {
if (str_contains($container, '-pr-')) { if ($server->isFunctional()) {
return explode('-pr-', $container)[1]; $this->servers = $this->servers->push($server);
} }
return $container; }
});
} else if (data_get($this->parameters, 'database_uuid')) { } else if (data_get($this->parameters, 'database_uuid')) {
$this->type = 'database'; $this->type = 'database';
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first(); $resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
@ -74,7 +81,9 @@ class Logs extends Component
} }
$this->resource = $resource; $this->resource = $resource;
$this->status = $this->resource->status; $this->status = $this->resource->status;
$this->server = $this->resource->destination->server; if ($this->resource->destination->server->isFunctional()) {
$this->servers = $this->servers->push($this->resource->destination->server);
}
$this->container = $this->resource->uuid; $this->container = $this->resource->uuid;
$this->containers->push($this->container); $this->containers->push($this->container);
} else if (data_get($this->parameters, 'service_uuid')) { } else if (data_get($this->parameters, 'service_uuid')) {
@ -87,7 +96,9 @@ class Logs extends Component
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid')); $this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
}); });
$this->server = $this->resource->server; if ($this->resource->server->isFunctional()) {
$this->servers = $this->servers->push($this->resource->server);
}
} }
} }

View File

@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.230', 'release' => '4.0.0-beta.231',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.230'; return '4.0.0-beta.231';

View File

@ -27,10 +27,17 @@
<a :class="activeTab === 'source' && 'text-white'" <a :class="activeTab === 'source' && 'text-white'"
@click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a> @click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a>
@endif @endif
<a :class="activeTab === 'servers' && 'text-white'" <a :class="activeTab === 'servers' && 'text-white'" class="flex items-center gap-2"
@click.prevent="activeTab = 'servers'; window.location.hash = 'servers'" href="#">Servers @click.prevent="activeTab = 'servers'; window.location.hash = 'servers'" href="#">Servers
@if (str($application->status)->contains('degraded'))
<span title="Some servers are unavailable">
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16" />
</svg>
</span>
@endif
</a> </a>
<a :class="activeTab === 'scheduled-tasks' && 'text-white'" <a :class="activeTab === 'scheduled-tasks' && 'text-white'"
@click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'" @click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'"
href="#">Scheduled Tasks href="#">Scheduled Tasks

View File

@ -12,23 +12,44 @@
@elseif ($type === 'service') @elseif ($type === 'service')
<h2>Execute Command</h2> <h2>Execute Command</h2>
@endif @endif
@if (count($containers) > 0) <div x-init="$wire.loadContainers">
<form class="flex flex-col gap-2 pt-4" wire:submit='runCommand'> <div class="pt-4" wire:loading wire:target='loadContainers'>
<div class="flex gap-2"> Loading containers...
<x-forms.input placeholder="ls -l" autofocus id="command" label="Command" required /> </div>
<x-forms.input id="workDir" label="Working directory" /> <div wire:loading.remove wire:target='loadContainers'>
</div> @if (count($containers) > 0)
<x-forms.select label="Container" id="container" required> <form class="flex flex-col gap-2 pt-4" wire:submit='runCommand'>
<option disabled selected>Select container</option> <div class="flex gap-2">
@foreach ($containers as $container) <x-forms.input placeholder="ls -l" autofocus id="command" label="Command" required />
<option value="{{ $container }}">{{ $container }}</option> <x-forms.input id="workDir" label="Working directory" />
@endforeach </div>
</x-forms.select> <x-forms.select label="Container" id="container" required>
<x-forms.button type="submit">Run</x-forms.button> <option disabled selected>Select container</option>
</form> @if (data_get($this->parameters, 'application_uuid'))
@else @foreach ($containers as $container)
<div class="pt-4">No containers are not running.</div> <option value="{{ data_get($container, 'container.Names') }}">
@endif {{ data_get($container, 'container.Names') }}
</option>
@endforeach
@elseif(data_get($this->parameters, 'service_uuid'))
@foreach ($containers as $container)
<option value="{{ $container }}">
{{ $container }}
</option>
@endforeach
@else
<option value="{{ $container }}">
{{ $container }}
</option>
@endif
</x-forms.select>
<x-forms.button type="submit">Run</x-forms.button>
</form>
@else
<div class="pt-4">No containers are not running.</div>
@endif
</div>
</div>
<div class="container w-full pt-10 mx-auto"> <div class="container w-full pt-10 mx-auto">
<livewire:activity-monitor header="Command output" /> <livewire:activity-monitor header="Command output" />
</div> </div>

View File

@ -1,7 +1,12 @@
<div> <div>
<div x-init="$wire.getLogs" id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }"> <div x-init="$wire.getLogs" id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<h3>{{ str($container)->beforeLast('-')->headline() }}</h3> @if ($resource->type() === 'application')
<h3>{{ $container }}</h3>
@else
<h3>{{ str($container)->beforeLast('-')->headline() }}</h3>
@endif
<div>Server: {{ $server->name }} </div>
@if ($pull_request) @if ($pull_request)
<div>({{ $pull_request }})</div> <div>({{ $pull_request }})</div>
@endif @endif

View File

@ -3,14 +3,20 @@
<h1>Logs</h1> <h1>Logs</h1>
<livewire:project.application.heading :application="$resource" /> <livewire:project.application.heading :application="$resource" />
<div class="pt-4"> <div class="pt-4">
@forelse ($containers as $container) <h2 class="pb-4">Logs</h2>
@if ($loop->first) <div class="pt-2" wire:loading wire:target="loadContainers">
<h2 class="pb-4">Logs</h2> Loading containers...
@endif </div>
<livewire:project.shared.get-logs :server="$server" :resource="$resource" :container="$container" /> @foreach ($servers as $server)
@empty <h3 x-init="$wire.loadContainers({{ $server->id }})"></h3>
<div>No containers are not running.</div> <div wire:loading.remove wire:target="loadContainers">
@endforelse @forelse (data_get($server,'containers',[]) as $container)
<livewire:project.shared.get-logs :server="$server" :resource="$resource" :container="data_get($container,'Names')" />
@empty
<div class="pt-2">No containers are not running on server: {{$server->name}}</div>
@endforelse
</div>
@endforeach
</div> </div>
@elseif ($type === 'database') @elseif ($type === 'database')
<h1>Logs</h1> <h1>Logs</h1>
@ -20,9 +26,9 @@
@if ($loop->first) @if ($loop->first)
<h2 class="pb-4">Logs</h2> <h2 class="pb-4">Logs</h2>
@endif @endif
<livewire:project.shared.get-logs :server="$server" :resource="$resource" :container="$container" /> <livewire:project.shared.get-logs :server="$servers[0]" :resource="$resource" :container="$container" />
@empty @empty
<div>No containers are not running.</div> <div class="pt-2">No containers are not running.</div>
@endforelse @endforelse
</div> </div>
@elseif ($type === 'service') @elseif ($type === 'service')
@ -31,9 +37,9 @@
@if ($loop->first) @if ($loop->first)
<h2 class="pb-4">Logs</h2> <h2 class="pb-4">Logs</h2>
@endif @endif
<livewire:project.shared.get-logs :server="$server" :resource="$resource" :container="$container" /> <livewire:project.shared.get-logs :server="$servers[0]" :resource="$resource" :container="$container" />
@empty @empty
<div>No containers are not running.</div> <div class="pt-2">No containers are not running.</div>
@endforelse @endforelse
</div> </div>
@endif @endif

View File

@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.230" "version": "4.0.0-beta.231"
} }
} }
} }