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(remoteProcessScript($process)) ? implode(' ', remoteProcessScript($process)) : remoteProcessScript($process); 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(remoteProcessScript($process), "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');