Add support for command execution in containers.

This commit is contained in:
Stuart Rowlands 2023-12-06 15:42:14 -08:00
parent 31959f26d4
commit 22178df8ae
6 changed files with 151 additions and 0 deletions

View File

@ -0,0 +1,105 @@
<?php
namespace App\Http\Livewire\Project\Application;
use App\Models\Application;
use App\Models\Server;
use App\Models\Service;
use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use Livewire\Component;
class Command extends Component
{
public string $command;
public string $container;
public string $dir;
public $server;
public $servers = [];
protected $rules = [
'server' => 'required',
'container' => 'required',
'command' => 'required',
];
protected $validationAttributes = [
'server' => 'server',
'container' => 'container',
'command' => 'command',
];
public function mount()
{
$this->containers = 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;
$this->server = $this->resource->destination->server;
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
if ($containers->count() > 0) {
$containers->each(function ($container) {
$this->containers->push(str_replace('/', '', $container['Names']));
});
}
} else if (data_get($this->parameters, 'database_uuid')) {
$this->type = 'database';
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
if (is_null($resource)) {
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
if (is_null($resource)) {
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
if (is_null($resource)) {
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
if (is_null($resource)) {
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
if (is_null($resource)) {
abort(404);
}
}
}
}
}
$this->resource = $resource;
$this->status = $this->resource->status;
$this->server = $this->resource->destination->server;
$this->container = $this->resource->uuid;
$this->containers->push($this->container);
} else if (data_get($this->parameters, 'service_uuid')) {
$this->type = 'service';
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
$service_name = data_get($this->parameters, 'service_name');
$this->serviceSubType = $this->resource->applications()->where('name', $service_name)->first();
if (!$this->serviceSubType) {
$this->serviceSubType = $this->resource->databases()->where('name', $service_name)->first();
}
$this->status = $this->resource->status;
$this->server = $this->resource->server;
$this->container = data_get($this->parameters, 'service_name') . '-' . $this->resource->uuid;
$this->containers->push($this->container);
}
}
public function runCommand()
{
$this->validate();
try {
if (!empty($this->dir)) {
$exec = "docker exec -w {$this->dir} {$this->container} {$this->command}";
}
else {
$exec = "docker exec {$this->container} {$this->command}";
}
$activity = remote_process([$exec], $this->server, ignore_errors: true);
$this->emit('newMonitorActivity', $activity->id);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
}

View File

@ -11,6 +11,10 @@
href="{{ route('project.application.logs', $parameters) }}">
<button>Logs</button>
</a>
<a class="{{ request()->routeIs('project.application.command') ? 'text-white' : '' }}"
href="{{ route('project.application.command', $parameters) }}">
<button>Run command</button>
</a>
<x-applications.links :application="$application" />
<div class="flex-1"></div>
@if ($application->build_pack === 'dockercompose' && is_null($application->docker_compose_raw))

View File

@ -7,6 +7,10 @@
href="{{ route('project.database.logs', $parameters) }}">
<button>Logs</button>
</a>
<a class="{{ request()->routeIs('project.database.command') ? 'text-white' : '' }}"
href="{{ route('project.database.command', $parameters) }}">
<button>Run command</button>
</a>
@if (
$database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||

View File

@ -0,0 +1,30 @@
<div>
@if ($type === 'application')
<livewire:project.application.heading :application="$resource" />
@elseif ($type === 'database')
<livewire:project.database.heading :database="$resource" />
@elseif ($type === 'service')
<livewire:project.service.navbar :service="$resource" :parameters="$parameters" :query="$query" />
<div class="pt-5 pb-5">
<a class="{{ request()->routeIs('project.service.show') ? 'text-white' : '' }}"
href="{{ route('project.service.show', $parameters) }}">
<button><- Back</button>
</a>
</div>
@endif
<form class="flex flex-col justify-center gap-2 xl:items-end xl:flex-row" wire:submit.prevent='runCommand'>
<x-forms.input placeholder="ls -l" autofocus id="command" label="Command" required />
<x-forms.input id="dir" label="Working directory" />
<x-forms.select label="Container" id="container" required>
<option selected>Select container</option>
@foreach ($containers as $container)
<option value="{{ $container }}">{{ $container }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Execute Command
</x-forms.button>
</form>
<div class="container w-full pt-10 mx-auto">
<livewire:activity-monitor header="Command output" />
</div>
</div>

View File

@ -26,6 +26,10 @@
<button>Logs</button>
</a>
@endif
<a class="{{ request()->routeIs('project.service.command') ? 'text-white' : '' }}"
href="{{ route('project.service.command', $parameters) }}">
<button>Run command</button>
</a>
</div>
<div class="w-full pl-8">
@isset($serviceApplication)

View File

@ -6,6 +6,7 @@
use App\Http\Controllers\MagicController;
use App\Http\Controllers\ProjectController;
use App\Http\Livewire\Project\Application\Configuration as ApplicationConfiguration;
use App\Http\Livewire\Project\Application\Command as ApplicationCommand;
use App\Http\Livewire\Boarding\Index as BoardingIndex;
use App\Http\Livewire\Project\Service\Index as ServiceIndex;
use App\Http\Livewire\Project\Service\Show as ServiceShow;
@ -111,18 +112,21 @@
)->name('project.application.deployment');
Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/logs', Logs::class)->name('project.application.logs');
Route::get('/project/{project_uuid}/{environment_name}/application/{application_uuid}/command', ApplicationCommand::class)->name('project.application.command');
// Databases
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}', [DatabaseController::class, 'configuration'])->name('project.database.configuration');
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups', [DatabaseController::class, 'backups'])->name('project.database.backups.all');
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/backups/{backup_uuid}', [DatabaseController::class, 'executions'])->name('project.database.backups.executions');
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/logs', Logs::class)->name('project.database.logs');
Route::get('/project/{project_uuid}/{environment_name}/database/{database_uuid}/command', ApplicationCommand::class)->name('project.database.command');
// Services
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}', ServiceIndex::class)->name('project.service.configuration');
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}', ServiceShow::class)->name('project.service.show');
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}/logs', Logs::class)->name('project.service.logs');
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/{service_name}/command', ApplicationCommand::class)->name('project.service.command');
});
Route::middleware(['auth'])->group(function () {