mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-17 02:14:05 +00:00
feat(v5): gate V5 to development environments only
Introduce V5Feature and config so V5 routes, jobs, commands, morph maps, and model queries run only when enabled. Move V5 migrations to migrations-v5 (loaded only when enabled), remove Flux from production Docker/install paths, and add isolation tests.
This commit is contained in:
1 parent
1cb1d028de
commit
d0247d3b09
45 files changed
+273
-218
No files matched your search
@@ -93,7 +93,7 @@ it('rewrites legacy fqcn morph rows to the v5 application alias', function () {
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$migration = include database_path('migrations/2026_07_06_090000_v5_convert_resource_connection_morphs_to_aliases.php');
|
||||
$migration = include database_path('migrations-v5/2026_07_06_090000_v5_convert_resource_connection_morphs_to_aliases.php');
|
||||
$migration->up();
|
||||
|
||||
$connection = DB::table('v5_resource_connections')->where('uuid', 'legacy-connection')->first();
|
||||
@@ -155,7 +155,7 @@ it('backfills the capability booleans from the legacy json column', function ()
|
||||
['name' => 'empty', 'capabilities' => null],
|
||||
]);
|
||||
|
||||
$migration = include database_path('migrations/2026_07_06_090100_v5_convert_server_capabilities_to_booleans.php');
|
||||
$migration = include database_path('migrations-v5/2026_07_06_090100_v5_convert_server_capabilities_to_booleans.php');
|
||||
$migration->up();
|
||||
|
||||
$serversByName = DB::table('v5_servers')->get()->keyBy('name');
|
||||
|
||||
@@ -7,7 +7,7 @@ beforeEach(function () {
|
||||
});
|
||||
|
||||
it('reuses existing projects instead of creating v5 projects', function () {
|
||||
expect(file_exists(database_path('migrations/2026_06_04_050157_v5_create_projects_table.php')))->toBeFalse()
|
||||
expect(file_exists(database_path('migrations-v5/2026_06_04_050157_v5_create_projects_table.php')))->toBeFalse()
|
||||
->and(file_exists(app_path('Models/V5/Project.php')))->toBeFalse();
|
||||
});
|
||||
|
||||
@@ -16,10 +16,10 @@ it('creates v5 cluster tables and lets each server belong to one cluster', funct
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_clusters'))->toBeTrue()
|
||||
@@ -61,10 +61,10 @@ it('creates v5 server tables in the shared database', function () {
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$migration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$migration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$migration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_servers'))->toBeTrue()
|
||||
@@ -109,10 +109,10 @@ it('creates v5 server canvas columns for movable caddy ingress nodes', function
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
expect(Schema::hasColumns('v5_servers', [
|
||||
@@ -127,10 +127,10 @@ it('creates v5 server ingress columns', function () {
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
expect(Schema::hasColumns('v5_servers', [
|
||||
@@ -147,13 +147,13 @@ it('creates v5 application tables for dashboard canvas nodes', function () {
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
$applicationMigration = include database_path('migrations/2026_06_19_140000_v5_create_applications_table.php');
|
||||
$applicationMigration = include database_path('migrations-v5/2026_06_19_140000_v5_create_applications_table.php');
|
||||
$applicationMigration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_applications'))->toBeTrue()
|
||||
@@ -188,13 +188,13 @@ it('creates v5 application domain tables for zero or more inbound routes', funct
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
$applicationMigration = include database_path('migrations/2026_06_19_140000_v5_create_applications_table.php');
|
||||
$applicationMigration = include database_path('migrations-v5/2026_06_19_140000_v5_create_applications_table.php');
|
||||
$applicationMigration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_application_domains'))->toBeTrue()
|
||||
@@ -221,16 +221,16 @@ it('creates generic v5 resource connection tables', function () {
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
$applicationMigration = include database_path('migrations/2026_06_19_140000_v5_create_applications_table.php');
|
||||
$applicationMigration = include database_path('migrations-v5/2026_06_19_140000_v5_create_applications_table.php');
|
||||
$applicationMigration->up();
|
||||
|
||||
$connectionMigration = include database_path('migrations/2026_06_19_142000_v5_create_resource_connections_table.php');
|
||||
$connectionMigration = include database_path('migrations-v5/2026_06_19_142000_v5_create_resource_connections_table.php');
|
||||
$connectionMigration->up();
|
||||
|
||||
expect(Schema::hasTable('v5_resource_connections'))->toBeTrue()
|
||||
@@ -269,10 +269,10 @@ it('keeps v5 server fields in the initial migration', function () {
|
||||
Schema::dropIfExists('v5_servers');
|
||||
Schema::dropIfExists('v5_clusters');
|
||||
|
||||
$clusterMigration = include database_path('migrations/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration = include database_path('migrations-v5/2026_06_16_130649_v5_create_clusters_table.php');
|
||||
$clusterMigration->up();
|
||||
|
||||
$serverMigration = include database_path('migrations/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration = include database_path('migrations-v5/2026_06_16_130650_v5_create_servers_table.php');
|
||||
$serverMigration->up();
|
||||
|
||||
expect(Schema::hasColumns('v5_servers', [
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
function inspectV5RegistrationForEnvironment(string $environment): array
|
||||
{
|
||||
$script = <<<'PHP'
|
||||
$environment = $argv[1];
|
||||
putenv("APP_ENV={$environment}");
|
||||
$_ENV['APP_ENV'] = $environment;
|
||||
$_SERVER['APP_ENV'] = $environment;
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
$app = require 'bootstrap/app.php';
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
|
||||
echo json_encode([
|
||||
'routes' => collect($app['router']->getRoutes()->getRoutes())
|
||||
->map(fn ($route) => $route->uri())
|
||||
->values()
|
||||
->all(),
|
||||
'migration_paths' => $app->make('migrator')->paths(),
|
||||
'horizon_supervisors' => array_keys(config("horizon.environments.{$environment}", [])),
|
||||
], JSON_THROW_ON_ERROR);
|
||||
PHP;
|
||||
|
||||
$process = new Process([PHP_BINARY, '-r', $script, $environment], base_path());
|
||||
$process->mustRun();
|
||||
|
||||
return json_decode($process->getOutput(), true, flags: JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
it('registers v5 routes, migrations, and workers in development environments', function (string $environment) {
|
||||
$registration = inspectV5RegistrationForEnvironment($environment);
|
||||
|
||||
expect($registration['routes'])
|
||||
->toContain('v5')
|
||||
->toContain('api/v1/internal/flux/resource-status')
|
||||
->and($registration['migration_paths'])->toContain(database_path('migrations-v5'))
|
||||
->and($registration['horizon_supervisors'])->toContain('v5reconcile');
|
||||
})->with(['local', 'development', 'dev', 'testing']);
|
||||
|
||||
it('does not register v5 routes, migrations, or workers outside development', function (string $environment) {
|
||||
$registration = inspectV5RegistrationForEnvironment($environment);
|
||||
|
||||
expect($registration['routes'])
|
||||
->not->toContain('v5')
|
||||
->not->toContain('api/v1/internal/flux/resource-status')
|
||||
->and($registration['migration_paths'])->not->toContain(database_path('migrations-v5'))
|
||||
->and($registration['horizon_supervisors'])->not->toContain('v5reconcile');
|
||||
})->with(['production', 'staging']);
|
||||
|
||||
it('keeps v5 schema changes out of the default migration path', function () {
|
||||
$v5MigrationFiles = collect(glob(database_path('migrations/*.php')))
|
||||
->filter(fn (string $path) => str_contains((string) file_get_contents($path), "'v5_"));
|
||||
|
||||
expect($v5MigrationFiles)->toBeEmpty();
|
||||
});
|
||||
|
||||
it('does not ship the v5 Flux runtime in the production container', function () {
|
||||
$productionCompose = file_get_contents(base_path('docker-compose.prod.yml'));
|
||||
$productionDockerfile = file_get_contents(base_path('docker/production/Dockerfile'));
|
||||
|
||||
expect($productionCompose)
|
||||
->not->toContain('COOLIFY_FLUX')
|
||||
->not->toContain('/data/coolify/flux')
|
||||
->and($productionDockerfile)
|
||||
->not->toContain('COOLIFY_FLUX_VERSION')
|
||||
->not->toContain('/usr/local/bin/flux');
|
||||
});
|
||||
|
||||
it('blocks v5 console commands when v5 is disabled', function (string $command) {
|
||||
config()->set('v5.enabled', false);
|
||||
|
||||
expect(Artisan::call($command))->toBe(1)
|
||||
->and(Artisan::output())->toContain('V5 is only available in development environments.');
|
||||
})->with([
|
||||
'flux:dev',
|
||||
'v5:flux-generate-keys',
|
||||
'v5:sync-dev-lima-servers',
|
||||
]);
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\Support\V5TestSchema;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('keeps v4 project and environment checks working without the v5 schema', function () {
|
||||
config()->set('v5.enabled', false);
|
||||
|
||||
$team = Team::factory()->create();
|
||||
$project = Project::factory()->create(['team_id' => $team->id]);
|
||||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||||
|
||||
V5TestSchema::dropAllTables();
|
||||
|
||||
expect($project->isEmpty())->toBeTrue()
|
||||
->and($environment->isEmpty())->toBeTrue();
|
||||
});
|
||||
@@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Schema;
|
||||
* build their schema by hand instead of running the full migration set.
|
||||
*
|
||||
* The column definitions must exactly mirror the final state produced by the
|
||||
* database/migrations/*v5* files (including the 2026_07_05/2026_07_06
|
||||
* database/migrations-v5 files (including the 2026_07_05/2026_07_06
|
||||
* additions: status_observed_at, coold_version, has_coold/is_ingress booleans
|
||||
* replacing the dropped capabilities json, and NOT NULL server uuids). Update
|
||||
* this class in the same change as any v5 migration.
|
||||
|
||||
Reference in new issue
Block a user