chore(v5): archive V5 implementation and remove runtime integration

Move V5 source, migrations, UI, scripts, and tests into documentation, then remove V5 routes, models, jobs, configuration, dependencies, and application hooks.
This commit is contained in:
Andras Bacsai committed 2026-08-15 19:13:30 +02:00
1 parent 1440d35750
commit 76030a30d4
237 files changed
+288 -7079

No files matched your search

@@ -0,0 +1,54 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('v5_clusters', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
$table->foreignId('created_by_user_id')->constrained('users')->cascadeOnDelete();
$table->string('name');
$table->text('description')->nullable();
$table->string('wireguard_interface')->default('wg0');
$table->string('wireguard_management_pool')->default('100.64.0.0/16');
$table->unsignedInteger('wireguard_listen_port')->default(51820);
$table->string('container_network_pool')->default('10.210.0.0/16');
$table->unsignedTinyInteger('container_network_prefix')->default(24);
$table->json('namespaces')->nullable();
$table->boolean('default_deny_containers')->default(true);
$table->string('coold_version')->default('nightly');
$table->string('corrosion_version')->default('v1.0.0');
$table->unsignedInteger('corrosion_gossip_port')->default(8787);
$table->unsignedInteger('corrosion_api_port')->default(8080);
$table->boolean('builder_enabled')->default(true);
$table->unsignedInteger('builder_capacity')->default(2);
$table->string('builder_cpu_quota')->default('200%');
$table->string('builder_memory_max')->default('2G');
$table->unsignedInteger('builder_timeout_secs')->default(1800);
$table->string('last_cli_action')->nullable();
$table->string('last_cli_status')->nullable();
$table->text('last_cli_summary')->nullable();
$table->timestamp('last_cli_ran_at')->nullable();
$table->timestamps();
$table->unique(['team_id', 'name']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('v5_clusters');
}
};
@@ -0,0 +1,63 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('v5_servers', function (Blueprint $table) {
$table->id();
$table->string('uuid')->nullable()->unique();
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
$table->foreignId('cluster_id')->nullable()->constrained('v5_clusters')->nullOnDelete();
$table->foreignId('created_by_user_id')->constrained('users')->cascadeOnDelete();
$table->foreignId('private_key_id')->nullable()->constrained('private_keys')->nullOnDelete();
$table->string('name');
$table->string('host');
$table->string('ssh_user');
$table->unsignedInteger('ssh_port')->default(22);
$table->string('status')->default('installed');
$table->string('ingress_type')->nullable();
$table->string('ingress_status')->nullable();
$table->json('capabilities')->nullable();
$table->boolean('builder_enabled')->default(false);
$table->unsignedInteger('builder_capacity')->default(0);
$table->string('builder_cpu_quota')->default('200%');
$table->string('node_address')->nullable();
$table->unsignedInteger('wireguard_listen_port_override')->nullable();
$table->string('wireguard_endpoint_override')->nullable();
$table->string('wireguard_management_ip')->nullable();
$table->string('wireguard_public_key')->nullable();
$table->json('container_subnets')->nullable();
$table->integer('canvas_x')->nullable();
$table->integer('canvas_y')->nullable();
$table->timestamp('last_bootstrapped_at')->nullable();
$table->string('last_bootstrap_action')->nullable();
$table->string('last_bootstrap_status')->nullable();
$table->text('last_bootstrap_output')->nullable();
$table->timestamp('last_bootstrap_ran_at')->nullable();
$table->string('last_status_check')->nullable();
$table->text('last_status_output')->nullable();
$table->timestamp('last_status_checked_at')->nullable();
$table->timestamps();
$table->unique(['team_id', 'host', 'ssh_port']);
$table->index(['team_id', 'cluster_id']);
$table->index(['team_id', 'status']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('v5_servers');
}
};
@@ -0,0 +1,58 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('v5_applications', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->foreignId('environment_id')->constrained('environments')->cascadeOnDelete();
$table->foreignId('server_id')->nullable()->constrained('v5_servers')->nullOnDelete();
$table->foreignId('created_by_user_id')->constrained('users')->cascadeOnDelete();
$table->string('name');
$table->string('image');
$table->string('container_name')->unique();
$table->string('status')->default('creating');
$table->text('status_message')->nullable();
$table->string('runtime_container_id')->nullable();
$table->string('mesh_namespace')->default('default');
$table->boolean('ingress_enabled')->default(false);
$table->unsignedSmallInteger('internal_port')->nullable();
$table->integer('canvas_x')->default(0);
$table->integer('canvas_y')->default(0);
$table->timestamps();
$table->index(['team_id', 'status']);
$table->index(['team_id', 'project_id', 'environment_id']);
$table->index(['team_id', 'server_id']);
});
Schema::create('v5_application_domains', function (Blueprint $table) {
$table->id();
$table->foreignId('application_id')->constrained('v5_applications')->cascadeOnDelete();
$table->string('domain');
$table->timestamps();
$table->unique(['application_id', 'domain']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('v5_application_domains');
Schema::dropIfExists('v5_applications');
}
};
@@ -0,0 +1,59 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('v5_resource_connections', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
$table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
$table->foreignId('environment_id')->constrained('environments')->cascadeOnDelete();
$table->morphs('resource_one');
$table->morphs('resource_two');
$table->string('resource_pair_key');
$table->foreignId('created_by_user_id')->constrained('users')->cascadeOnDelete();
$table->timestamps();
$table->unique(['team_id', 'resource_pair_key']);
$table->index(['team_id', 'project_id', 'environment_id']);
});
Schema::create('v5_resource_connection_rules', function (Blueprint $table) {
$table->id();
$table->foreignId('connection_id')->constrained('v5_resource_connections')->cascadeOnDelete();
$table->morphs('source_resource');
$table->morphs('target_resource');
$table->string('protocol')->default('tcp');
$table->unsignedSmallInteger('port');
$table->timestamps();
$table->unique([
'connection_id',
'source_resource_type',
'source_resource_id',
'target_resource_type',
'target_resource_id',
'protocol',
'port',
], 'v5_resource_connection_rules_unique_direction_port');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('v5_resource_connection_rules');
Schema::dropIfExists('v5_resource_connections');
}
};
@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('v5_container_statuses', function (Blueprint $table) {
$table->id();
$table->foreignId('team_id')->constrained('teams')->cascadeOnDelete();
$table->foreignId('server_id')->constrained('v5_servers')->cascadeOnDelete();
$table->string('container_id');
$table->string('container_name')->nullable();
$table->string('image')->nullable();
$table->string('status')->default('unknown');
$table->text('status_message')->nullable();
$table->timestamp('last_seen_at')->nullable();
$table->timestamps();
$table->unique(['server_id', 'container_id']);
$table->index(['team_id', 'server_id']);
$table->index(['team_id', 'status']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('v5_container_statuses');
}
};
@@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->index('wireguard_management_ip');
$table->index('node_address');
$table->index('host');
});
Schema::table('v5_applications', function (Blueprint $table) {
$table->index('runtime_container_id');
});
Schema::table('v5_container_statuses', function (Blueprint $table) {
$table->index('last_seen_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropIndex(['wireguard_management_ip']);
$table->dropIndex(['node_address']);
$table->dropIndex(['host']);
});
Schema::table('v5_applications', function (Blueprint $table) {
$table->dropIndex(['runtime_container_id']);
});
Schema::table('v5_container_statuses', function (Blueprint $table) {
$table->dropIndex(['last_seen_at']);
});
}
};
@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
DB::table('v5_servers')
->whereNull('uuid')
->pluck('id')
->each(function (int $id): void {
DB::table('v5_servers')
->where('id', $id)
->update(['uuid' => Str::lower(Str::random(24))]);
});
Schema::table('v5_servers', function (Blueprint $table) {
$table->string('uuid')->nullable(false)->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->string('uuid')->nullable()->change();
});
}
};
@@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->timestamp('status_observed_at')->nullable()->after('status');
});
Schema::table('v5_applications', function (Blueprint $table) {
$table->timestamp('status_observed_at')->nullable()->after('status_message');
});
Schema::table('v5_container_statuses', function (Blueprint $table) {
$table->timestamp('status_observed_at')->nullable()->after('status_message');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropColumn('status_observed_at');
});
Schema::table('v5_applications', function (Blueprint $table) {
$table->dropColumn('status_observed_at');
});
Schema::table('v5_container_statuses', function (Blueprint $table) {
$table->dropColumn('status_observed_at');
});
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->string('coold_version')->nullable()->after('wireguard_public_key');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropColumn('coold_version');
});
}
};
@@ -0,0 +1,74 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* Rewrites v5 polymorphic rows that stored the Application FQCN (written
* before the 'v5.application' morph alias existed) so a future class rename
* cannot orphan them. New rows pick up the alias via getMorphClass().
*/
return new class extends Migration
{
private const FQCN = 'App\Models\V5\Application';
private const ALIAS = 'v5.application';
public function up(): void
{
$this->rewriteMorphTypes(self::FQCN, self::ALIAS);
$this->rewritePairKeys(self::FQCN, self::ALIAS);
}
public function down(): void
{
$this->rewriteMorphTypes(self::ALIAS, self::FQCN);
$this->rewritePairKeys(self::ALIAS, self::FQCN);
}
private function rewriteMorphTypes(string $from, string $to): void
{
if (Schema::hasTable('v5_resource_connections')) {
foreach (['resource_one_type', 'resource_two_type'] as $column) {
DB::table('v5_resource_connections')
->where($column, $from)
->update([$column => $to]);
}
}
if (Schema::hasTable('v5_resource_connection_rules')) {
foreach (['source_resource_type', 'target_resource_type'] as $column) {
DB::table('v5_resource_connection_rules')
->where($column, $from)
->update([$column => $to]);
}
}
}
private function rewritePairKeys(string $from, string $to): void
{
if (! Schema::hasTable('v5_resource_connections')) {
return;
}
// Backslash escaping in LIKE patterns differs between Postgres and
// SQLite, so match the FQCN in PHP instead of in SQL.
DB::table('v5_resource_connections')
->select(['id', 'resource_pair_key'])
->orderBy('id')
->chunkById(100, function ($connections) use ($from, $to): void {
foreach ($connections as $connection) {
if (! str_contains((string) $connection->resource_pair_key, $from)) {
continue;
}
DB::table('v5_resource_connections')
->where('id', $connection->id)
->update([
'resource_pair_key' => str_replace($from, $to, $connection->resource_pair_key),
]);
}
});
}
};
@@ -0,0 +1,74 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* Replaces the unindexable v5_servers.capabilities JSON array of magic strings
* with indexed has_coold / is_ingress booleans. The Server model keeps
* exposing a computed `capabilities` array so serialized payloads stay
* identical.
*/
return new class extends Migration
{
public function up(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->boolean('has_coold')->default(false)->index();
$table->boolean('is_ingress')->default(false)->index();
});
DB::table('v5_servers')
->select(['id', 'capabilities'])
->orderBy('id')
->chunkById(100, function ($servers): void {
foreach ($servers as $server) {
$capabilities = json_decode($server->capabilities ?? '[]', true);
if (! is_array($capabilities) || $capabilities === []) {
continue;
}
DB::table('v5_servers')->where('id', $server->id)->update([
'has_coold' => in_array('coold', $capabilities, true),
'is_ingress' => in_array('ingress', $capabilities, true),
]);
}
});
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropColumn('capabilities');
});
}
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->json('capabilities')->nullable();
});
DB::table('v5_servers')
->select(['id', 'has_coold', 'is_ingress'])
->orderBy('id')
->chunkById(100, function ($servers): void {
foreach ($servers as $server) {
$capabilities = array_values(array_filter([
$server->has_coold ? 'coold' : null,
$server->is_ingress ? 'ingress' : null,
]));
DB::table('v5_servers')->where('id', $server->id)->update([
'capabilities' => json_encode($capabilities),
]);
}
});
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropIndex(['has_coold']);
$table->dropIndex(['is_ingress']);
$table->dropColumn(['has_coold', 'is_ingress']);
});
}
};
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->string('agent_token_jti')->nullable()->after('coold_version');
});
}
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropColumn('agent_token_jti');
});
}
};
@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('v5_revoked_agent_tokens', function (Blueprint $table) {
$table->id();
$table->string('jti')->unique();
$table->foreignId('server_id')->nullable();
$table->timestamp('revoked_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('v5_revoked_agent_tokens');
}
};
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->timestamp('agent_token_expires_at')->nullable()->after('agent_token_jti');
});
}
public function down(): void
{
Schema::table('v5_servers', function (Blueprint $table) {
$table->dropColumn('agent_token_expires_at');
});
}
};
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* The uuid columns were added by editing the original v5 create migrations,
* so databases that ran those migrations before the edit (including the
* checked-in testing schema dump) are missing them. Guarded so freshly
* migrated databases are untouched.
*/
return new class extends Migration
{
private const TABLES = ['v5_applications', 'v5_clusters', 'v5_resource_connections'];
public function up(): void
{
foreach (self::TABLES as $tableName) {
if (! Schema::hasTable($tableName) || Schema::hasColumn($tableName, 'uuid')) {
continue;
}
Schema::table($tableName, function (Blueprint $table) {
$table->string('uuid')->nullable()->unique();
});
}
}
public function down(): void
{
// Intentionally left empty: the uuid columns belong to the create
// migrations; dropping them here could destroy live identifiers.
}
};
+8
View File
@@ -0,0 +1,8 @@
# Archived V5 Migration Prototypes
These files preserve the previous V5 schema work for reference only. Their
`.php.txt` extension prevents Laravel from discovering or running them as
migrations.
Do not register or execute these files. V5 database setup will be implemented
using a different strategy.