Functional scheduled executions.
Display last executions. Support for Services.
This commit is contained in:
parent
9bbe9567c7
commit
e2e6813632
@ -3,17 +3,18 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\ScheduledTaskExecution;
|
||||
use App\Models\Server;
|
||||
use App\Models\Application;
|
||||
use App\Models\Service;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Collection;
|
||||
use Throwable;
|
||||
|
||||
class ScheduledTaskJob implements ShouldQueue
|
||||
@ -25,13 +26,10 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
public ScheduledTask $task;
|
||||
public Application|Service $resource;
|
||||
|
||||
public ?string $container_name = null;
|
||||
public ?string $directory_name = null;
|
||||
public ?ScheduledTaskExecution $backup_log = null;
|
||||
public ?ScheduledTaskExecution $task_log = null;
|
||||
public string $task_status = 'failed';
|
||||
public int $size = 0;
|
||||
public ?string $backup_output = null;
|
||||
public ?S3Storage $s3 = null;
|
||||
public ?string $task_output = null;
|
||||
public array $containers = [];
|
||||
|
||||
public function __construct($task)
|
||||
{
|
||||
@ -41,6 +39,7 @@ public function __construct($task)
|
||||
} else if ($application = $task->application()->first()) {
|
||||
$this->resource = $application;
|
||||
}
|
||||
$this->team = Team::find($task->team_id);
|
||||
}
|
||||
|
||||
public function middleware(): array
|
||||
@ -55,23 +54,62 @@ public function uniqueId(): int
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
file_put_contents('/tmp/scheduled-job-run', 'ran in handle');
|
||||
try {
|
||||
echo($this->resource->type());
|
||||
file_put_contents('/tmp/scheduled-job-run-'.$this->task->id, $this->task->name);
|
||||
$this->task_log = ScheduledTaskExecution::create([
|
||||
'scheduled_task_id' => $this->task->id,
|
||||
]);
|
||||
|
||||
$this->server = $this->resource->destination->server;
|
||||
|
||||
if ($this->resource->type() == 'application') {
|
||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
||||
if ($containers->count() > 0) {
|
||||
$containers->each(function ($container) {
|
||||
$this->containers[] = str_replace('/', '', $container['Names']);
|
||||
});
|
||||
}
|
||||
}
|
||||
elseif ($this->resource->type() == 'service') {
|
||||
$this->resource->applications()->get()->each(function ($application) {
|
||||
if (str(data_get($application, 'status'))->contains('running')) {
|
||||
$this->containers[] = data_get($application, 'name') . '-' . data_get($this->resource, 'uuid');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (count($this->containers) == 0) {
|
||||
throw new \Exception('ScheduledTaskJob failed: No containers running.');
|
||||
}
|
||||
|
||||
if (count($this->containers) > 1 && empty($this->task->container)) {
|
||||
throw new \Exception('ScheduledTaskJob failed: More than one container exists but no container name was provided.');
|
||||
}
|
||||
|
||||
foreach ($this->containers as $containerName) {
|
||||
if (count($this->containers) == 1 || str_starts_with($containerName, $this->task->container . '-' . $this->resource->uuid)) {
|
||||
$cmd = 'sh -c "' . str_replace('"', '\"', $this->task->command) . '"';
|
||||
$exec = "docker exec {$containerName} {$cmd}";
|
||||
$this->task_output = instant_remote_process([$exec], $this->server, true);
|
||||
$this->task_log->update([
|
||||
'status' => 'success',
|
||||
'message' => $this->task_output,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No valid container was found.
|
||||
throw new \Exception('ScheduledTaskJob failed: No valid container was found. Is the container name correct?');
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->task_log) {
|
||||
$this->task_log->update([
|
||||
'status' => 'failed',
|
||||
'message' => $this->task_output ?? $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
||||
throw $e;
|
||||
} finally {
|
||||
// BackupCreated::dispatch($this->team->id);
|
||||
}
|
||||
}
|
||||
private function add_to_backup_output($output): void
|
||||
{
|
||||
if ($this->backup_output) {
|
||||
$this->backup_output = $this->backup_output . "\n" . $output;
|
||||
} else {
|
||||
$this->backup_output = $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ public function submit($data)
|
||||
$task->command = $data['command'];
|
||||
$task->frequency = $data['frequency'];
|
||||
$task->container = $data['container'];
|
||||
$task->team_id = currentTeam()->id;
|
||||
|
||||
switch ($this->resource->type()) {
|
||||
case 'application':
|
||||
|
28
app/Livewire/Project/Shared/ScheduledTask/Executions.php
Normal file
28
app/Livewire/Project/Shared/ScheduledTask/Executions.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Component;
|
||||
|
||||
class Executions extends Component
|
||||
{
|
||||
public $backup;
|
||||
public $executions = [];
|
||||
public $selectedKey;
|
||||
public function getListeners()
|
||||
{
|
||||
return [
|
||||
"selectTask",
|
||||
];
|
||||
}
|
||||
|
||||
public function selectTask($key): void
|
||||
{
|
||||
if ($key == $this->selectedKey) {
|
||||
$this->selectedKey = null;
|
||||
return;
|
||||
}
|
||||
$this->selectedKey = $key;
|
||||
}
|
||||
}
|
@ -396,6 +396,10 @@ public function byName(string $name)
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function scheduled_tasks(): HasMany
|
||||
{
|
||||
return $this->hasMany(ScheduledTask::class)->orderBy('name', 'asc');
|
||||
}
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
||||
|
@ -23,6 +23,7 @@ public function up(): void
|
||||
|
||||
$table->foreignId('application_id')->nullable();
|
||||
$table->foreignId('service_id')->nullable();
|
||||
$table->foreignId('team_id');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,10 @@
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
<a :class="activeTab === 'scheduled-tasks' && 'text-white'"
|
||||
@click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'"
|
||||
href="#">Scheduled Tasks
|
||||
</a>
|
||||
<a :class="activeTab === 'danger' && 'text-white'"
|
||||
@click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'"
|
||||
@ -161,6 +165,9 @@ class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||
<livewire:project.shared.environment-variable.all :resource="$service" />
|
||||
</div>
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'scheduled-tasks'">
|
||||
<livewire:project.shared.scheduled-task.all :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'danger'">
|
||||
<livewire:project.shared.danger :resource="$service" />
|
||||
</div>
|
||||
|
@ -8,24 +8,18 @@
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@forelse($resource->scheduled_tasks as $task)
|
||||
<a class="flex flex-col box"
|
||||
|
||||
@if ($resource->type() == 'application')
|
||||
href="{{ route('project.application.scheduled-tasks', [...$parameters, 'task_uuid' => $task->uuid]) }}">
|
||||
@elseif ($resource->type() == 'service')
|
||||
href="{{ route('project.service.scheduled-tasks', [...$parameters, 'task_uuid' => $task->uuid]) }}">
|
||||
@endif
|
||||
<div><span class="font-bold text-warning">{{ $task->name }}<span></div>
|
||||
<div>Frequency: {{ $task->frequency }}</div>
|
||||
<div>Last run: {{ data_get($task->latest_log, 'status', 'No runs yet') }}</div>
|
||||
<div>Next run: @todo</div>
|
||||
</a>
|
||||
@empty
|
||||
<div>No scheduled tasks configured.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
{{-- @if ($type === 'service-database' && $selectedBackup)
|
||||
<div class="pt-10">
|
||||
<livewire:project.database.backup-edit key="{{ $selectedBackup->id }}" :backup="$selectedBackup" :s3s="$s3s"
|
||||
:status="data_get($database, 'status')" />
|
||||
<h3 class="py-4">Executions</h3>
|
||||
<livewire:project.database.backup-executions key="{{ $selectedBackup->id }}" :backup="$selectedBackup"
|
||||
:executions="$selectedBackup->executions" />
|
||||
</div>
|
||||
@endif --}}
|
||||
</div>
|
||||
|
@ -0,0 +1,27 @@
|
||||
<div class="flex flex-col-reverse gap-2">
|
||||
@forelse($executions as $execution)
|
||||
<a class="flex flex-col box" wire:click="selectTask({{ data_get($execution, 'id') }})"
|
||||
@class([
|
||||
'border-green-500' => data_get($execution, 'status') === 'success',
|
||||
'border-red-500' => data_get($execution, 'status') === 'failed',
|
||||
])>
|
||||
@if (data_get($execution, 'status') === 'running')
|
||||
<div class="absolute top-2 right-2">
|
||||
<x-loading />
|
||||
</div>
|
||||
@endif
|
||||
<div>Status: {{ data_get($execution, 'status') }}</div>
|
||||
<div>Started At: {{ data_get($execution, 'created_at') }}</div>
|
||||
@if (data_get($execution, 'id') == $selectedKey)
|
||||
@if (data_get($execution, 'message'))
|
||||
<div>Output: <pre>{{ data_get($execution, 'message') }}</pre></div>
|
||||
@else
|
||||
<div>No output was recorded for this execution.</div>
|
||||
@endif
|
||||
@endif
|
||||
</a>
|
||||
</a>
|
||||
@empty
|
||||
<div>No executions found.</div>
|
||||
@endforelse
|
||||
</div>
|
@ -7,7 +7,11 @@ class="font-bold text-warning">({{ $task->name }})</span>?</p>
|
||||
</x-modal>
|
||||
|
||||
<h1>Scheduled Backup</h1>
|
||||
@if ($type === 'application')
|
||||
<livewire:project.application.heading :application="$resource" />
|
||||
@elseif ($type === 'service')
|
||||
<livewire:project.service.navbar :service="$resource" :parameters="$parameters" />
|
||||
@endif
|
||||
|
||||
<form wire:submit="submit">
|
||||
<div class="flex flex-col gap-2 pb-10">
|
||||
@ -17,10 +21,6 @@ class="font-bold text-warning">({{ $task->name }})</span>?</p>
|
||||
Save
|
||||
</x-forms.button>
|
||||
|
||||
{{-- @if (Str::of($status)->startsWith('running'))
|
||||
<livewire:project.database.backup-now :backup="$backup" />
|
||||
@endif --}}
|
||||
|
||||
<x-forms.button isError isModal modalId="{{ $modalId }}">
|
||||
Delete
|
||||
</x-forms.button>
|
||||
@ -33,4 +33,10 @@ class="font-bold text-warning">({{ $task->name }})</span>?</p>
|
||||
<x-forms.input placeholder="0 0 * * * or daily" id="task.frequency" label="Frequency" required />
|
||||
<x-forms.input placeholder="php" id="task.container" label="Container name" />
|
||||
</form>
|
||||
|
||||
<div class="pt-10">
|
||||
<h3 class="py-4">Recent executions</h3>
|
||||
<livewire:project.shared.scheduled-task.executions key="{{ $task->id }}" selectedKey=""
|
||||
:executions="$task->executions->take(-20)" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -143,6 +143,8 @@
|
||||
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}/command', ExecuteContainerCommand::class)->name('project.service.command');
|
||||
Route::get('/project/{project_uuid}/{environment_name}/service/{service_uuid}/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.service.scheduled-tasks');
|
||||
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
|
Loading…
Reference in New Issue
Block a user