From cf4c6600df51c731df15cbc36d35168dc19a4e6f Mon Sep 17 00:00:00 2001 From: Christoph Bratschi Date: Thu, 10 Sep 2026 23:15:39 +0200 Subject: [PATCH] fix(database): use MongoDB writable-primary health check --- app/Actions/Database/StartMongodb.php | 43 +++++++++-- tests/Unit/StartMongodbHealthCheckTest.php | 89 ++++++++++++++++++++++ 2 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 tests/Unit/StartMongodbHealthCheckTest.php diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 097e19f7b2..baf1bcc53b 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -109,11 +109,7 @@ class StartMongodb $this->database->destination->network, ], 'labels' => defaultDatabaseLabels($this->database)->toArray(), - 'healthcheck' => $this->database->healthCheckConfiguration([ - 'CMD', - 'echo', - 'ok', - ]), + 'healthcheck' => $this->database->healthCheckConfiguration($this->generate_health_check_command()), 'mem_limit' => $this->database->limits_memory, 'memswap_limit' => $this->database->limits_memory_swap, 'mem_swappiness' => $this->database->limits_memory_swappiness, @@ -324,6 +320,43 @@ class StartMongodb return $environment_variables->all(); } + private function generate_health_check_command(): array + { + $usesLegacyShell = str($this->database->image)->startsWith('mongo:4'); + $command = [ + 'CMD', + $usesLegacyShell ? 'mongo' : 'mongosh', + '--quiet', + '--host', + $this->database->uuid, + ]; + + if ($this->database->enable_ssl) { + $command = [ + ...$command, + $usesLegacyShell ? '--ssl' : '--tls', + $usesLegacyShell ? '--sslCAFile' : '--tlsCAFile', + '/etc/mongo/certs/ca.pem', + ]; + + if ($this->database->ssl_mode === 'verify-full') { + $command = [ + ...$command, + $usesLegacyShell ? '--sslPEMKeyFile' : '--tlsCertificateKeyFile', + '/etc/mongo/certs/server.pem', + ]; + } + } + + return [ + ...$command, + '--eval', + $usesLegacyShell + ? 'quit(db.isMaster().ismaster === true ? 0 : 1)' + : 'quit(db.hello().isWritablePrimary === true ? 0 : 1)', + ]; + } + private function add_custom_mongo_conf() { if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) { diff --git a/tests/Unit/StartMongodbHealthCheckTest.php b/tests/Unit/StartMongodbHealthCheckTest.php new file mode 100644 index 0000000000..b2f687bdab --- /dev/null +++ b/tests/Unit/StartMongodbHealthCheckTest.php @@ -0,0 +1,89 @@ +toContain("'healthcheck' => \$this->database->healthCheckConfiguration(\$this->generate_health_check_command())") + ->not->toContain("'healthcheck' => \$this->database->healthCheckConfiguration([\n 'CMD',\n 'echo',\n 'ok'"); +}); + +function generatedMongodbHealthCheckCommand(array $attributes): array +{ + $action = new StartMongodb; + $action->database = new StandaloneMongodb; + $action->database->setRawAttributes($attributes, true); + $method = new ReflectionMethod(StartMongodb::class, 'generate_health_check_command'); + + return $method->invoke($action); +} + +it('generates a credential-free writable-primary MongoDB health check', function (string $image, bool $enableSsl, ?string $sslMode, array $expectedCommand) { + $command = generatedMongodbHealthCheckCommand([ + 'uuid' => 'mongodb-test-resource', + 'image' => $image, + 'enable_ssl' => $enableSsl, + 'ssl_mode' => $sslMode, + 'mongo_initdb_root_username' => 'root-user', + 'mongo_initdb_root_password' => 'root-password', + ]); + + expect($command)->toBe($expectedCommand) + ->and(implode(' ', $command)) + ->not->toContain('root-user') + ->not->toContain('root-password') + ->not->toContain('--tlsAllowInvalidHostnames') + ->not->toContain('--tlsAllowInvalidCertificates'); +})->with([ + 'without TLS' => ['mongo:8', false, null, [ + 'CMD', 'mongosh', '--quiet', '--host', 'mongodb-test-resource', '--eval', + 'quit(db.hello().isWritablePrimary === true ? 0 : 1)', + ]], + 'TLS allow mode' => ['mongo:8', true, 'allow', [ + 'CMD', 'mongosh', '--quiet', '--host', 'mongodb-test-resource', + '--tls', '--tlsCAFile', '/etc/mongo/certs/ca.pem', '--eval', + 'quit(db.hello().isWritablePrimary === true ? 0 : 1)', + ]], + 'TLS prefer mode' => ['mongo:8', true, 'prefer', [ + 'CMD', 'mongosh', '--quiet', '--host', 'mongodb-test-resource', + '--tls', '--tlsCAFile', '/etc/mongo/certs/ca.pem', '--eval', + 'quit(db.hello().isWritablePrimary === true ? 0 : 1)', + ]], + 'TLS require mode' => ['mongo:8', true, 'require', [ + 'CMD', 'mongosh', '--quiet', '--host', 'mongodb-test-resource', + '--tls', '--tlsCAFile', '/etc/mongo/certs/ca.pem', '--eval', + 'quit(db.hello().isWritablePrimary === true ? 0 : 1)', + ]], + 'TLS verify-full mode' => ['mongo:8', true, 'verify-full', [ + 'CMD', 'mongosh', '--quiet', '--host', 'mongodb-test-resource', + '--tls', + '--tlsCAFile', + '/etc/mongo/certs/ca.pem', + '--tlsCertificateKeyFile', + '/etc/mongo/certs/server.pem', + '--eval', + 'quit(db.hello().isWritablePrimary === true ? 0 : 1)', + ]], + 'MongoDB 4 without TLS' => ['mongo:4.4', false, null, [ + 'CMD', 'mongo', '--quiet', '--host', 'mongodb-test-resource', '--eval', + 'quit(db.isMaster().ismaster === true ? 0 : 1)', + ]], + 'MongoDB 4 TLS require mode' => ['mongo:4.4', true, 'require', [ + 'CMD', 'mongo', '--quiet', '--host', 'mongodb-test-resource', + '--ssl', '--sslCAFile', '/etc/mongo/certs/ca.pem', '--eval', + 'quit(db.isMaster().ismaster === true ? 0 : 1)', + ]], + 'MongoDB 4 TLS verify-full mode' => ['mongo:4.4', true, 'verify-full', [ + 'CMD', 'mongo', '--quiet', '--host', 'mongodb-test-resource', + '--ssl', + '--sslCAFile', + '/etc/mongo/certs/ca.pem', + '--sslPEMKeyFile', + '/etc/mongo/certs/server.pem', + '--eval', + 'quit(db.isMaster().ismaster === true ? 0 : 1)', + ]], +]);