mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
A custom config file written with tee gets the server umask. With umask 027 or 077 the file is not readable by the container user, so MySQL and MariaDB ignored the whole config without an error. - Set mode 644 on the MySQL, MariaDB, PostgreSQL, and MongoDB custom config files after each write. - Close the multiplexed SSH connection when the server user, IP, or port changes, so the next command does not run as the old user. Closes #11600
120 lines
5.8 KiB
PHP
120 lines
5.8 KiB
PHP
<?php
|
|
|
|
use App\Actions\Database\StartDragonfly;
|
|
use App\Actions\Database\StartKeydb;
|
|
use App\Actions\Database\StartMariadb;
|
|
use App\Actions\Database\StartMongodb;
|
|
use App\Actions\Database\StartMysql;
|
|
use App\Actions\Database\StartPostgresql;
|
|
use App\Actions\Database\StartRedis;
|
|
use App\Models\Environment;
|
|
use App\Models\InstanceSettings;
|
|
use App\Models\PrivateKey;
|
|
use App\Models\Project;
|
|
use App\Models\Server;
|
|
use App\Models\StandaloneDocker;
|
|
use App\Models\Team;
|
|
use App\Services\DatabaseStartCommandExecutor;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Process;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Spatie\Activitylog\Models\Activity;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
InstanceSettings::forceCreate(['id' => 0]);
|
|
config(['app.maintenance.store' => 'array', 'cache.default' => 'array', 'constants.ssh.mux_enabled' => false]);
|
|
// The queued ServerStorageSaveJob does not run, as when it runs after the containers start.
|
|
Queue::fake();
|
|
|
|
// Every path on the server is missing, and every path is inside the resource directory.
|
|
Process::fake(function ($process) {
|
|
$command = is_array($process->command) ? implode(' ', $process->command) : $process->command;
|
|
if (str_contains($command, 'empty-directory')) {
|
|
return Process::result(output: collect(range(1, substr_count($command, '/ssl/server.')))->map(fn (int $index) => "{$index}:missing")->implode("\n"));
|
|
}
|
|
if (str_contains($command, 'readlink -f')) {
|
|
return Process::result(output: 'OK');
|
|
}
|
|
|
|
return Process::result(output: str_contains($command, 'test -') ? 'NOK' : '');
|
|
});
|
|
|
|
$team = Team::factory()->create();
|
|
$privateKey = PrivateKey::factory()->create(['team_id' => $team->id]);
|
|
$this->server = Server::factory()->create(['team_id' => $team->id, 'private_key_id' => $privateKey->id]);
|
|
$this->destination = StandaloneDocker::query()->where('server_id', $this->server->id)->firstOrFail();
|
|
$project = Project::factory()->create(['team_id' => $team->id]);
|
|
$this->environment = Environment::factory()->create(['project_id' => $project->id]);
|
|
|
|
$this->executor = new class
|
|
{
|
|
public array $commands = [];
|
|
|
|
public function execute(array $commands, $database, Activity $activity): Activity
|
|
{
|
|
$this->commands = $commands;
|
|
|
|
return $activity;
|
|
}
|
|
};
|
|
app()->instance(DatabaseStartCommandExecutor::class, $this->executor);
|
|
});
|
|
|
|
dataset('ssl-database-start-actions-with-certificate-files', [
|
|
'postgresql' => ['create_standalone_postgresql', StartPostgresql::class, ['server.crt', 'server.key']],
|
|
'mysql' => ['create_standalone_mysql', StartMysql::class, ['server.crt', 'server.key']],
|
|
'mariadb' => ['create_standalone_mariadb', StartMariadb::class, ['server.crt', 'server.key']],
|
|
'mongodb' => ['create_standalone_mongodb', StartMongodb::class, ['server.pem']],
|
|
'redis' => ['create_standalone_redis', StartRedis::class, ['server.crt', 'server.key']],
|
|
'keydb' => ['create_standalone_keydb', StartKeydb::class, ['server.crt', 'server.key']],
|
|
'dragonfly' => ['create_standalone_dragonfly', StartDragonfly::class, ['server.crt', 'server.key']],
|
|
]);
|
|
|
|
it('writes a new SSL certificate file on the server before the container starts', function (string $createDatabase, string $startAction, array $files) {
|
|
$database = $createDatabase($this->environment->id, $this->destination);
|
|
$database->update(['enable_ssl' => true]);
|
|
|
|
$startAction::run($database->fresh(), new Activity);
|
|
|
|
foreach ($files as $file) {
|
|
$path = escapeshellarg($database->workdir()."/ssl/{$file}");
|
|
Process::assertRan(fn ($process) => str_contains($process->command, "base64 -d | tee {$path}"));
|
|
}
|
|
$commands = implode("\n", $this->executor->commands);
|
|
expect($commands)->toContain('missing configuration file')
|
|
->and(strpos($commands, 'missing configuration file'))->toBeLessThan(strpos($commands, 'docker-compose.yml up -d'));
|
|
})->with('ssl-database-start-actions-with-certificate-files');
|
|
|
|
it('writes the MongoDB server.pem before the certificate owner change mounts it', function () {
|
|
$database = create_standalone_mongodb($this->environment->id, $this->destination);
|
|
$database->update(['enable_ssl' => true]);
|
|
|
|
StartMongodb::run($database->fresh(), new Activity);
|
|
|
|
$commands = implode("\n", $this->executor->commands);
|
|
expect($commands)->toContain('Writing 1 missing configuration file.')
|
|
->and(strpos($commands, 'Writing 1 missing configuration file.'))->toBeLessThan(strpos($commands, '--entrypoint chown'));
|
|
});
|
|
|
|
dataset('database-start-actions-with-custom-config-files', [
|
|
'postgresql' => ['create_standalone_postgresql', StartPostgresql::class, 'postgres_conf', 'custom-postgres.conf'],
|
|
'mysql' => ['create_standalone_mysql', StartMysql::class, 'mysql_conf', 'custom-config.cnf'],
|
|
'mariadb' => ['create_standalone_mariadb', StartMariadb::class, 'mariadb_conf', 'custom-config.cnf'],
|
|
'mongodb' => ['create_standalone_mongodb', StartMongodb::class, 'mongo_conf', 'mongod.conf'],
|
|
]);
|
|
|
|
it('makes the custom config file readable by the container user before the container starts', function (string $createDatabase, string $startAction, string $column, string $file) {
|
|
$database = $createDatabase($this->environment->id, $this->destination);
|
|
$database->update([$column => "# custom\n"]);
|
|
|
|
$startAction::run($database->fresh(), new Activity);
|
|
|
|
$path = $database->workdir()."/{$file}";
|
|
$commands = implode("\n", $this->executor->commands);
|
|
expect($commands)->toContain("chmod 644 {$path}")
|
|
->and(strpos($commands, "tee {$path}"))->toBeLessThan(strpos($commands, "chmod 644 {$path}"))
|
|
->and(strpos($commands, "chmod 644 {$path}"))->toBeLessThan(strpos($commands, 'docker-compose.yml up -d'));
|
|
})->with('database-start-actions-with-custom-config-files');
|