Skip to content

Commit

Permalink
feat: #11 allow to retry tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
bohdan-shulha committed Jun 21, 2024
1 parent d36bd24 commit dc3f9f2
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 28 deletions.
10 changes: 9 additions & 1 deletion app/Casts/TaskMetaCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public function get(Model $model, string $key, mixed $value, array $attributes):
throw new InvalidArgumentException('Model must be an instance of NodeTask');
}

return self::META_BY_TYPE[$model->type]::from($value);
// if (!isset($attributes['type'])) {
// return null;
// }

return self::META_BY_TYPE[$attributes['type']]::from($value);
}

/**
Expand All @@ -43,6 +47,10 @@ public function set(Model $model, string $key, mixed $value, array $attributes):
throw new InvalidArgumentException('Model must be an instance of NodeTask');
}

if (is_string($value)) {
return $value;
}

return $value->toJson();
}
}
10 changes: 9 additions & 1 deletion app/Casts/TaskPayloadCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ public function get(Model $model, string $key, mixed $value, array $attributes):
throw new InvalidArgumentException('Model must be an instance of NodeTask');
}

return self::PAYLOAD_BY_TYPE[$model->type]::from($value);
// if (!isset($attributes['type'])) {
// return null;
// }
//dd($model->type, $attributes);
return self::PAYLOAD_BY_TYPE[$attributes['type']]::from($value);
}

/**
Expand All @@ -46,6 +50,10 @@ public function set(Model $model, string $key, mixed $value, array $attributes):
throw new InvalidArgumentException('Model must be an instance of NodeTask');
}

if (is_string($value)) {
return $value;
}

return $value->toJson();
}
}
10 changes: 9 additions & 1 deletion app/Casts/TaskResultCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ public function get(Model $model, string $key, mixed $value, array $attributes):
return ErrorResult::from($value);
}

// if (!isset($attributes['type'])) {
// return null;
// }

if ($value != null) {
return self::RESULT_BY_TYPE[$model->type]::from($value);
return self::RESULT_BY_TYPE[$attributes['type']]::from($value);
}

return null;
Expand All @@ -50,6 +54,10 @@ public function set(Model $model, string $key, mixed $value, array $attributes):
throw new InvalidArgumentException('Model must be an instance of NodeTask');
}

if (is_string($value)) {
return $value;
}

return $value->toJson();
}
}
11 changes: 10 additions & 1 deletion app/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

namespace App\Http\Controllers;

use App\Models\User;

abstract class Controller
{
//
protected function authorizeOr403(string $ability, ...$arguments)
{
$user = auth()->user();

if ($user->cannot($ability, ...$arguments)) {
abort(403);
}
}
}
9 changes: 6 additions & 3 deletions app/Http/Controllers/NodeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ public function store(StoreNodeRequest $request)
*/
public function show(Node $node)
{
$initTaskGroup = $node->tasks()->inProgress()->ofType(InitSwarmTaskPayload::class)->first()?->taskGroup->with('tasks')->first();
if (!$initTaskGroup) {
$initTaskGroup = $node->tasks()->unsuccessful()->ofType(InitSwarmTaskPayload::class)->first()?->taskGroup->with('tasks')->first();
$initTaskGroup = null;
if (is_null($node->swarm_id)) {
$initTaskGroup = $node->tasks()->inProgress()->ofType(InitSwarmTaskPayload::class)->first()?->taskGroup->load(['tasks', 'invoker']);
if (!$initTaskGroup) {
$initTaskGroup = $node->tasks()->unsuccessful()->ofType(InitSwarmTaskPayload::class)->latest('id')->first()?->taskGroup->load(['tasks', 'invoker']);
}
}

return Inertia::render('Nodes/Show', ['node' => $node, 'initTaskGroup' => $initTaskGroup]);
Expand Down
27 changes: 27 additions & 0 deletions app/Http/Controllers/NodeTaskGroupController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Http\Controllers;

use App\Models\Node;
use App\Models\NodeTaskGroup;
use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\Request;

class NodeTaskGroupController extends Controller
{
public function retry(Request $request, NodeTaskGroup $taskGroup)
{
$this->authorizeOr403('retry', $taskGroup);

$attrs = $request->validate([
'node_id' => 'required|exists:nodes,id',
]);

$node = Node::whereId($attrs['node_id'])->first();

$this->authorizeOr403('view', $node);

$taskGroup->retry($node);
}
}
18 changes: 9 additions & 9 deletions app/Models/NodeTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ class NodeTask extends Model

protected static function booted()
{
self::creating(function (NodeTask $nodeTask) {
$payload = $nodeTask->payload;

if (!($payload instanceof AbstractTaskPayload)) {
throw new IllegalArgumentException('Payload must be an instance of AbstractTaskPayload');
}

$nodeTask->type = TaskPayloadCast::TYPE_BY_PAYLOAD[get_class($payload)];
});
// self::creating(function (NodeTask $nodeTask) {
// $payload = $nodeTask->payload;
//
// if (!($payload instanceof AbstractTaskPayload)) {
// throw new IllegalArgumentException('Payload must be an instance of AbstractTaskPayload');
// }
//
// $nodeTask->type = TaskPayloadCast::TYPE_BY_PAYLOAD[get_class($payload)];
// });
}

public function taskGroup(): BelongsTo
Expand Down
42 changes: 42 additions & 0 deletions app/Models/NodeTaskGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Symfony\Component\VarDumper\VarDumper;
use function Psy\debug;

class NodeTaskGroup extends Model
{
Expand Down Expand Up @@ -43,11 +45,51 @@ public function node(): BelongsTo
return $this->belongsTo(Node::class);
}

public function invoker(): BelongsTo
{
return $this->belongsTo(User::class, 'invoker_id');
}

public function start(Node $node): void
{
$this->status = TaskStatus::Running;
$this->node_id = $node->id;
$this->started_at = now();
$this->save();
}

public function retry(Node|null $node): void
{
$nodeId = is_null($node) ? null : $node->id;

$taskGroup = new NodeTaskGroup();
$taskGroup->node_id = $nodeId;
$taskGroup->forceFill(collect($this->attributes)->only([
'swarm_id',
'invoker_id',
])->toArray());
$taskGroup->save();


$taskGroup->tasks()->saveMany($this->tasks->map(function (NodeTask $task) use ($node) {
$dataAttrs = $task->is_completed
? [
'status',
'started_at',
'ended_at',
'result'
]
: [
];

$attrs = collect($task->attributes);

$attributes = $attrs
->only($dataAttrs)
->merge($attrs->only(['type', 'meta', 'payload']))
->toArray();

return (new NodeTask($attributes))->forceFill($attributes);
}));
}
}
17 changes: 17 additions & 0 deletions app/Policies/NodeTaskGroupPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php


namespace App\Policies;

use App\Models\Node;
use App\Models\NodeTaskGroup;
use App\Models\User;
use Illuminate\Auth\Access\Response;

class NodeTaskGroupPolicy
{
public function retry(User $user, NodeTaskGroup $taskGroup): bool
{
return $user->belongsToTeam($taskGroup->node->team);
}
}
5 changes: 5 additions & 0 deletions app/Traits/HasTaskStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public function getIsRunningAttribute() : bool
return $this->status === TaskStatus::Running;
}

public function getIsCompletedAttribute() : bool
{
return $this->status === TaskStatus::Completed;
}

public function scopeOfType(Builder $query, string $typeClass): Builder
{
return $query->where('type', TaskPayloadCast::TYPE_BY_PAYLOAD[$typeClass]);
Expand Down
16 changes: 15 additions & 1 deletion resources/js/Components/NodeTasks/TaskGroup.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
<script setup>
import {reactive} from "vue";
import TaskResult from "@/Components/NodeTasks/TaskResult.vue";
import SecondaryButton from "@/Components/SecondaryButton.vue";
import {router} from "@inertiajs/vue3";
defineProps({
const props = defineProps({
'taskGroup': Object,
});
const retry = () => {
router.post(route('node-task-groups.retry', {taskGroup: props.taskGroup.id, node_id: props.taskGroup.node_id}));
}
</script>

<template>
<ul class="col-span-6 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<TaskResult v-for="task in taskGroup.tasks" :key="task.id" :task="task" />
</ul>
<div class="flex">
<div class="col-span-6 ms-4 mt-1 text-xs text-gray-900 dark:text-white grow">#{{ taskGroup.id }} Invoked by {{ taskGroup.invoker.name }}</div>
<SecondaryButton v-if="taskGroup.status === 'failed' || taskGroup.status === 'canceled'"
@click="retry"
class="col-span-6 ms-4 mt-2 text-xs text-gray-900 dark:text-white">
Retry Failed Tasks
</SecondaryButton>
</div>
</template>
3 changes: 3 additions & 0 deletions resources/js/Components/NodeTasks/TaskResult.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const classes = computed(() => {

<span v-html="task.formatted_payload" class="grow" />

<span class="text-xs me-2 text-gray-500">#{{ task.id }}</span>

<span v-auto-animate="{duration: 100}" v-if="task.result">
<svg v-if="state.expanded"
class="w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
Expand All @@ -75,6 +77,7 @@ const classes = computed(() => {
</svg>

</span>
<span v-else class="w-4"></span>
</div>
<div v-if="state.expanded" class="px-4 py-2 border-t border-t-gray-100 bg-gray-50" v-html="task.formatted_result" />
</li>
Expand Down
20 changes: 20 additions & 0 deletions resources/js/Pages/Nodes/Partials/SwarmDetauls.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

<script setup>
import ActionSection from "@/Components/ActionSection.vue";
</script>

<template>
<ActionSection>
<template #title>
Swarm Cluster
</template>

<template #description>
Swarm Cluster has been initialized and is in a healthy state.
</template>

<template #content>
Sorry, we're still working on the Swarm Cluster details block.
</template>
</ActionSection>
</template>
16 changes: 5 additions & 11 deletions resources/js/Pages/Nodes/Show.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
<script setup>
import AppLayout from '@/Layouts/AppLayout.vue';
import PrimaryButton from "@/Components/PrimaryButton.vue";
import FormSection from "@/Components/FormSection.vue";
import NewSwarmCluster from "@/Pages/Nodes/Partials/NewSwarmCluster.vue";
import ServerDetailsForm from "@/Pages/Nodes/Partials/ServerDetailsForm.vue";
import LayoutTab from "@/Components/LayoutTab.vue";
import ShowLayout from "@/Pages/Nodes/ShowLayout.vue";
import { CopyClipboard } from 'flowbite';
import {onMounted, ref} from "vue";
import AgentInstall from "@/Pages/Nodes/Partials/AgentInstall.vue";
import AgentStatus from "@/Pages/Nodes/Partials/AgentStatus.vue";
import SectionBorder from "@/Components/SectionBorder.vue";
import InitSwarmProgress from "@/Pages/Nodes/Partials/InitSwarmProgress.vue";
import SwarmDetauls from "@/Pages/Nodes/Partials/SwarmDetauls.vue";
defineProps([
'node',
'initTaskGroup',
]);
</script>

<template>
Expand All @@ -32,7 +23,10 @@ defineProps([

<SectionBorder v-if="$props.node.online" />

<NewSwarmCluster v-if="$props.node.online && $props.node.swarm_id === null" :node="$props.node"/>
<template v-if="$props.node.online">
<NewSwarmCluster v-if="$props.node.swarm_id === null" :node="$props.node"/>
<InitSwarmProgress v-if="$props.initTaskGroup" :taskGroup="$props.initTaskGroup" />
<SwarmDetauls v-if="$props.node.swarm_id !== null" :node="$props.node"/>
</template>
</ShowLayout>
</template>
3 changes: 3 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use App\Http\Controllers\NodeController;
use App\Http\Controllers\NodeTaskGroupController;
use App\Http\Controllers\SwarmTaskController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
Expand All @@ -26,5 +27,7 @@

Route::post('/swarm-tasks/init-cluster', [SwarmTaskController::class, 'initCluster'])->name('swarm-tasks.init-cluster');

Route::post('/node-task-groups/{taskGroup}/retry', [NodeTaskGroupController::class, 'retry'])->name('node-task-groups.retry');

Route::resource("nodes", NodeController::class);
});

0 comments on commit dc3f9f2

Please sign in to comment.