Skip to content

Commit

Permalink
feat: #122 rework swarm-related management interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
bohdan-shulha committed Aug 20, 2024
1 parent f493dd3 commit 16bc52c
Show file tree
Hide file tree
Showing 18 changed files with 336 additions and 365 deletions.
3 changes: 0 additions & 3 deletions app/Http/Controllers/NodeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use App\Models\Node;
use App\Models\NodeTaskGroupType;
use App\Models\Service;
use App\Models\Swarm;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
Expand Down Expand Up @@ -87,13 +86,11 @@ public function show(Node $node)

return Inertia::render('Nodes/Show', [
'node' => $node,
'swarms' => Swarm::all(),
'isLastNode' => $node->team->nodes->count() === 1,
'initTaskGroup' => $initTaskGroup ?: $joinTaskGroup,
'lastAgentVersion' => $lastAgentVersion,
'agentUpgradeTaskGroup' => $taskGroup?->is_completed ? null : $taskGroup,
'registryUpdateTaskGroup' => $registryTaskGroup?->is_completed ? null : $registryTaskGroup,
'swarmsQuotaReached' => auth()->user()->currentTeam->quotas()->swarms->quotaReached(),
]);
}

Expand Down
23 changes: 14 additions & 9 deletions app/Http/Controllers/ServiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,18 @@ public function index()
*/
public function create()
{
$swarms = Swarm::all();
$swarm = auth()->user()->currentTeam->swarms()->first();

$networks = count($swarms) ? $swarms[0]->networks : [];
$nodes = count($swarms) ? $swarms[0]->nodes : [];
$dockerRegistries = count($swarms) ? $swarms[0]->data->registries : [];
$s3Storages = count($swarms) ? $swarms[0]->data->s3Storages : [];
$networks = $swarm->networks;
$nodes = $swarm->nodes;
$dockerRegistries = $swarm->data->registries;
$s3Storages = $swarm->data->s3Storages;

$deploymentData = DeploymentData::make([
'networkName' => count($networks) ? $networks[0]->name : null,
'networkName' => $networks->first()->name,
]);

return Inertia::render('Services/Create', [
'swarms' => $swarms,
'networks' => $networks,
'nodes' => $nodes,
'deploymentData' => $deploymentData,
Expand All @@ -62,15 +61,21 @@ public function store(StoreServiceRequest $request)
{
$deploymentData = DeploymentData::validateAndCreate($request->get('deploymentData'));

$team = auth()->user()->currentTeam;
$swarm = $team->swarms()->firstOrFail();

$service = Service::make($request->validated());
$service->team_id = auth()->user()->current_team_id;
$service->team_id = $team->id;
$service->swarm_id = $swarm->id;

DB::transaction(function () use ($service, $deploymentData) {
$service->save();

$service->deploy($deploymentData);
});

return to_route('services.deployments', ['service' => $service->id]);
return to_route('services.deployments', $service)
->with('success', 'Service created and deployment scheduled successfully.');
}

/**
Expand Down
18 changes: 12 additions & 6 deletions app/Http/Controllers/SwarmTaskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,22 @@ public function initCluster(InitClusterFormRequest $request)
public function joinCluster(JoinClusterFormRequest $request)
{
DB::transaction(function () use ($request) {
$node = Node::findOrFail($request->node_id);
$swarm = $node->team->swarms()->first();

if (! $swarm) {
throw new \Exception('No swarm found for the node\'s team.');
}

$taskGroup = NodeTaskGroup::create([
'type' => NodeTaskGroupType::JoinSwarm,
'swarm_id' => $request->swarm_id,
'team_id' => auth()->user()->currentTeam->id,
'node_id' => $request->node_id,
'swarm_id' => $swarm->id,
'team_id' => $node->team_id,
'node_id' => $node->id,
'invoker_id' => auth()->user()->id,
]);

$node = Node::findOrFail($request->node_id);
$node->swarm_id = $request->swarm_id;
$node->swarm_id = $swarm->id;
$node->save();

$remoteAddrs = collect($taskGroup->swarm->data->managerNodes)->map(fn (SwarmData\ManagerNode $node) => $node->addr)->toArray();
Expand All @@ -238,7 +244,7 @@ public function joinCluster(JoinClusterFormRequest $request)

$taskGroup->tasks()->create([
'type' => NodeTaskType::JoinSwarm,
'meta' => JoinSwarmMeta::from(['swarmId' => $request->swarm_id, 'role' => $request->role]),
'meta' => JoinSwarmMeta::from(['swarmId' => $swarm->id, 'role' => $request->role]),
'payload' => [
'JoinSpec' => [
'ListenAddr' => '0.0.0.0:2377',
Expand Down
1 change: 0 additions & 1 deletion app/Http/Requests/NodeTask/InitClusterFormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public function rules(): array
{
return [
'node_id' => ['required', 'exists:nodes,id'],
'name' => ['required', 'string', 'max:255'],
'advertise_addr' => ['required', 'ipv4'],
'force_new_cluster' => ['boolean'],
];
Expand Down
1 change: 0 additions & 1 deletion app/Http/Requests/NodeTask/JoinClusterFormRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public function rules(): array
{
return [
'node_id' => ['required', 'exists:nodes,id'],
'swarm_id' => ['required', 'exists:swarms,id'],
'role' => ['required', 'in:manager,worker'],
'advertise_addr' => ['exclude_if:role,worker', 'required', 'ipv4'],
];
Expand Down
1 change: 0 additions & 1 deletion app/Http/Requests/StoreServiceRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public function rules(): array
{
return [
'name' => ['required', 'string'],
'swarm_id' => ['required', 'exists:swarms,id'],
];
}
}
3 changes: 1 addition & 2 deletions app/Models/NodeTasks/InitSwarm/InitSwarmMeta.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ class InitSwarmMeta extends AbstractTaskMeta
{
public function __construct(
public int $swarmId,
public string $name,
public bool $forceNewCluster
) {}

public function formattedHtml(): string
{
$msg = 'Initialize Docker Swarm cluster <code>'.$this->name.'</code>';
$msg = 'Initialize Docker Swarm cluster';
if ($this->forceNewCluster) {
$msg .= ' with <b>force cluster creation</b>';
}
Expand Down
1 change: 0 additions & 1 deletion app/Models/Swarm.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class Swarm extends Model
use HasOwningTeam;

protected $fillable = [
'name',
'data',
'team_id',
];
Expand Down
2 changes: 1 addition & 1 deletion app/Notifications/TrialEndsSoonNotification.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function toMail(object $notifiable): MailMessage
$dateDiff = $nextPayment->date->longRelativeToNowDiffForHumans();

return (new MailMessage)
->subject("Your trial ends in {$dateDiff}")
->subject("Your free trial ends in {$dateDiff}")
->greeting("Hello {$this->team->customer->name}!")
->line('Your trial for team '.$this->team->name.' ends soon.')
->line("You will be charged {$nextPayment->amount()} on {$nextPayment->date->toDateTimeString()} ({$dateDiff}).")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('swarms', function (Blueprint $table) {
$table->dropColumn('name');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('swarms', function (Blueprint $table) {
$table->string('name')->after('id');
});

DB::table('swarms')->update(['name' => DB::raw('CAST(id AS VARCHAR)')]);

Schema::table('swarms', function (Blueprint $table) {
$table->string('name')->nullable(false)->change();
});
}
};
88 changes: 88 additions & 0 deletions resources/js/Pages/Nodes/Partials/InitSwarmCluster.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script setup>
import { defineProps } from "vue";
import { useForm } from "@inertiajs/vue3";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import FormSection from "@/Components/FormSection.vue";
import InputError from "@/Components/InputError.vue";
import { FwbCheckbox } from "flowbite-vue";
import Select from "@/Components/Select.vue";
import FormField from "@/Components/FormField.vue";
const props = defineProps({
node: Object,
});
const initForm = useForm({
node_id: props.node.id,
advertise_addr: "",
force_new_cluster: false,
});
const submit = () => {
initForm.post(route("swarm-tasks.init-cluster"), {
preserveScroll: "errors",
});
};
</script>

<template>
<FormSection @submitted="submit">
<template #title>Swarm Cluster</template>

<template #description>
This is your first node in the swarm cluster. Initializing a new
swarm cluster will make this node the manager node, responsible for
orchestrating and managing the cluster. You can add worker nodes to
this cluster later to distribute workloads efficiently.
</template>

<template #form>
<div v-auto-animate class="col-span-6 sm:col-span-4">
<div class="grid gap-4">
<template v-if="$props.node.data.host.networks.length > 0">
<FormField :error="initForm.errors.advertise_addr">
<template #label>Advertisement Address</template>
<Select
id="advertise_addr"
v-model="initForm.advertise_addr"
placeholder="Select Advertise Address"
>
<optgroup
v-for="network in $props.node.data.host
.networks"
:label="network.if_name"
>
<option
v-for="ip in network.ips"
:value="ip.ip"
>
{{ ip.ip }}
</option>
</optgroup>
</Select>
</FormField>

<FormField :error="initForm.errors.force_new_cluster">
<!-- TODO: add warning that the force new cluster will wipe out your existing cluster (services, data?) -->
<FwbCheckbox
label="Force New Cluster"
v-model="initForm.force_new_cluster"
/>
</FormField>
<InputError
v-if="initForm.force_new_cluster"
message="Be aware that you will lose your data!"
/>
</template>
<template v-else> No IP found </template>
</div>
</div>
</template>

<template #submit>
<PrimaryButton type="submit"
>Initialize Swarm Cluster</PrimaryButton
>
</template>
</FormSection>
</template>
76 changes: 76 additions & 0 deletions resources/js/Pages/Nodes/Partials/JoinSwarmCluster.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script setup>
import { defineProps } from "vue";
import { useForm } from "@inertiajs/vue3";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import FormSection from "@/Components/FormSection.vue";
import Select from "@/Components/Select.vue";
import FormField from "@/Components/FormField.vue";
const props = defineProps({
node: Object,
});
const joinForm = useForm({
node_id: props.node.id,
role: "manager",
advertise_addr: "",
});
const submit = () => {
joinForm.post(route("swarm-tasks.join-cluster"), {
preserveScroll: "errors",
});
};
</script>

<template>
<FormSection @submitted="submit">
<template #title>Swarm Cluster</template>

<template #description>
Connect this node to an existing Docker Swarm cluster, enabling
seamless integration and distributed computing capabilities.
</template>

<template #form>
<div class="col-span-6 sm:col-span-4">
<div class="grid gap-4">
<FormField :error="joinForm.errors.role">
<template #label> Role </template>

<Select v-model="joinForm.role">
<option value="manager">Manager</option>
<option value="worker">Worker</option>
</Select>
</FormField>

<FormField :error="joinForm.errors.advertise_addr">
<template #label>Advertisement Address</template>
<Select
id="advertise_addr"
v-model="joinForm.advertise_addr"
placeholder="Select Advertise Address"
>
<optgroup
v-for="network in $props.node.data.host
.networks"
:label="network.if_name"
>
<option
v-for="ip in network.ips"
:value="ip.ip"
>
{{ ip.ip }}
</option>
</optgroup>
</Select>
</FormField>
</div>
</div>
</template>

<template #submit>
<PrimaryButton type="submit">Join Swarm Cluster</PrimaryButton>
</template>
</FormSection>
</template>
Loading

0 comments on commit 16bc52c

Please sign in to comment.