Added ClickHouse Migration Support (#7392)

This commit is contained in:
Andras Bacsai 2025-12-17 11:37:53 +01:00 committed by GitHub
commit d4a403278d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 138 additions and 10 deletions

View file

@ -51,7 +51,7 @@ public function handle(StandaloneClickhouse $database)
],
'labels' => defaultDatabaseLabels($this->database)->toArray(),
'healthcheck' => [
'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
'test' => "clickhouse-client --user {$this->database->clickhouse_admin_user} --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
'interval' => '5s',
'timeout' => '5s',
'retries' => 10,
@ -152,12 +152,16 @@ private function generate_environment_variables()
$environment_variables->push("$env->key=$env->real_value");
}
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_USER'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_USER={$this->database->clickhouse_admin_user}");
}
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_PASSWORD'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_PASSWORD={$this->database->clickhouse_admin_password}");
}
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_DB'))->isEmpty()) {
$environment_variables->push("CLICKHOUSE_DB={$this->database->clickhouse_db}");
}
add_coolify_default_environment_variables($this->database, $environment_variables, $environment_variables);

View file

@ -25,7 +25,7 @@ protected static function booted()
static::created(function ($database) {
LocalPersistentVolume::create([
'name' => 'clickhouse-data-'.$database->uuid,
'mount_path' => '/bitnami/clickhouse',
'mount_path' => '/var/lib/clickhouse',
'host_path' => null,
'resource_id' => $database->id,
'resource_type' => $database->getMorphClass(),
@ -246,8 +246,8 @@ protected function internalDbUrl(): Attribute
get: function () {
$encodedUser = rawurlencode($this->clickhouse_admin_user);
$encodedPass = rawurlencode($this->clickhouse_admin_password);
return "clickhouse://{$encodedUser}:{$encodedPass}@{$this->uuid}:9000/{$this->clickhouse_db}";
$database = $this->clickhouse_db ?? 'default';
return "clickhouse://{$encodedUser}:{$encodedPass}@{$this->uuid}:9000/{$database}";
},
);
}
@ -263,8 +263,8 @@ protected function externalDbUrl(): Attribute
}
$encodedUser = rawurlencode($this->clickhouse_admin_user);
$encodedPass = rawurlencode($this->clickhouse_admin_password);
return "clickhouse://{$encodedUser}:{$encodedPass}@{$serverIp}:{$this->public_port}/{$this->clickhouse_db}";
$database = $this->clickhouse_db ?? 'default';
return "clickhouse://{$encodedUser}:{$encodedPass}@{$serverIp}:{$this->public_port}/{$database}";
}
return null;

View file

@ -0,0 +1,69 @@
<?php
use App\Models\LocalPersistentVolume;
use App\Models\StandaloneClickhouse;
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.
*
* Migrates existing ClickHouse instances from Bitnami/BinamiLegacy images
* to the official clickhouse/clickhouse-server image.
*/
public function up(): void
{
// Add clickhouse_db column if it doesn't exist
if (! Schema::hasColumn('standalone_clickhouses', 'clickhouse_db')) {
Schema::table('standalone_clickhouses', function (Blueprint $table) {
$table->string('clickhouse_db')
->default('default')
->after('clickhouse_admin_password');
});
}
// Change the default value for the 'image' column to the official image
Schema::table('standalone_clickhouses', function (Blueprint $table) {
$table->string('image')->default('clickhouse/clickhouse-server:25.11')->change();
});
// Update existing ClickHouse instances from Bitnami images to official image
StandaloneClickhouse::where(function ($query) {
$query->where('image', 'like', '%bitnami/clickhouse%')
->orWhere('image', 'like', '%bitnamilegacy/clickhouse%');
})
->update([
'image' => 'clickhouse/clickhouse-server:25.11',
'clickhouse_db' => DB::raw("COALESCE(clickhouse_db, 'default')"),
]);
// Update volume mount paths from Bitnami to official image paths
LocalPersistentVolume::where('resource_type', StandaloneClickhouse::class)
->where('mount_path', '/bitnami/clickhouse')
->update(['mount_path' => '/var/lib/clickhouse']);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Revert the default value for the 'image' column
Schema::table('standalone_clickhouses', function (Blueprint $table) {
$table->string('image')->default('bitnamilegacy/clickhouse')->change();
});
// Revert existing ClickHouse instances back to Bitnami image
StandaloneClickhouse::where('image', 'clickhouse/clickhouse-server:25.11')
->update(['image' => 'bitnamilegacy/clickhouse']);
// Revert volume mount paths
LocalPersistentVolume::where('resource_type', StandaloneClickhouse::class)
->where('mount_path', '/var/lib/clickhouse')
->update(['mount_path' => '/bitnami/clickhouse']);
}
};

View file

@ -0,0 +1,55 @@
<?php
use App\Models\StandaloneClickhouse;
test('clickhouse uses clickhouse_db field in internal connection string', function () {
$clickhouse = new StandaloneClickhouse();
$clickhouse->clickhouse_admin_user = 'testuser';
$clickhouse->clickhouse_admin_password = 'testpass';
$clickhouse->clickhouse_db = 'mydb';
$clickhouse->uuid = 'test-uuid';
$internalUrl = $clickhouse->internal_db_url;
expect($internalUrl)
->toContain('mydb')
->toContain('testuser')
->toContain('test-uuid');
});
test('clickhouse defaults to default database when clickhouse_db is null', function () {
$clickhouse = new StandaloneClickhouse();
$clickhouse->clickhouse_admin_user = 'testuser';
$clickhouse->clickhouse_admin_password = 'testpass';
$clickhouse->clickhouse_db = null;
$clickhouse->uuid = 'test-uuid';
$internalUrl = $clickhouse->internal_db_url;
expect($internalUrl)->toContain('/default');
});
test('clickhouse external url uses correct database', function () {
$clickhouse = new StandaloneClickhouse();
$clickhouse->clickhouse_admin_user = 'admin';
$clickhouse->clickhouse_admin_password = 'secret';
$clickhouse->clickhouse_db = 'production';
$clickhouse->uuid = 'prod-uuid';
$clickhouse->is_public = true;
$clickhouse->public_port = 8123;
$clickhouse->destination = new class {
public $server;
public function __construct() {
$this->server = new class {
public function __get($name) {
if ($name === 'getIp') return '1.2.3.4';
}
};
}
};
$externalUrl = $clickhouse->external_db_url;
expect($externalUrl)->toContain('production');
});