refactor(database-detection): enhance isDatabaseImage function to utilize service configuration for improved detection accuracy
This commit is contained in:
@@ -724,11 +724,12 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
|||||||
return $labels->all();
|
return $labels->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDatabaseImage(?string $image = null)
|
function isDatabaseImage(?string $image = null, ?array $serviceConfig = null)
|
||||||
{
|
{
|
||||||
if (is_null($image)) {
|
if (is_null($image)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$image = str($image);
|
$image = str($image);
|
||||||
if ($image->contains(':')) {
|
if ($image->contains(':')) {
|
||||||
$image = str($image);
|
$image = str($image);
|
||||||
@@ -736,13 +737,170 @@ function isDatabaseImage(?string $image = null)
|
|||||||
$image = str($image)->append(':latest');
|
$image = str($image)->append(':latest');
|
||||||
}
|
}
|
||||||
$imageName = $image->before(':');
|
$imageName = $image->before(':');
|
||||||
|
|
||||||
|
// First check if it's a known database image
|
||||||
|
$isKnownDatabase = false;
|
||||||
foreach (DATABASE_DOCKER_IMAGES as $database_docker_image) {
|
foreach (DATABASE_DOCKER_IMAGES as $database_docker_image) {
|
||||||
if (str($imageName)->contains($database_docker_image)) {
|
if (str($imageName)->contains($database_docker_image)) {
|
||||||
return true;
|
$isKnownDatabase = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// If no database pattern found, it's definitely not a database
|
||||||
|
if (! $isKnownDatabase) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have service configuration, use additional context to make better decisions
|
||||||
|
if (! is_null($serviceConfig)) {
|
||||||
|
return isDatabaseImageWithContext($imageName, $serviceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to original behavior for backward compatibility
|
||||||
|
return $isKnownDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDatabaseImageWithContext(string $imageName, array $serviceConfig): bool
|
||||||
|
{
|
||||||
|
// Known application images that contain database names but are not databases
|
||||||
|
$knownApplicationPatterns = [
|
||||||
|
// SuperTokens authentication
|
||||||
|
'supertokens/supertokens-mysql',
|
||||||
|
'supertokens/supertokens-postgresql',
|
||||||
|
'supertokens/supertokens-mongodb',
|
||||||
|
'registry.supertokens.io/supertokens/supertokens-mysql',
|
||||||
|
'registry.supertokens.io/supertokens/supertokens-postgresql',
|
||||||
|
'registry.supertokens.io/supertokens/supertokens-mongodb',
|
||||||
|
'registry.supertokens.io/supertokens',
|
||||||
|
|
||||||
|
// Analytics and BI tools
|
||||||
|
'metabase/metabase', // Uses databases but is not a database
|
||||||
|
'amancevice/superset', // Uses databases but is not a database
|
||||||
|
'nocodb/nocodb', // Uses databases but is not a database
|
||||||
|
'ghcr.io/umami-software/umami', // Web analytics with postgresql variant
|
||||||
|
|
||||||
|
// Secret management
|
||||||
|
'infisical/infisical', // Secret management with postgres variant
|
||||||
|
|
||||||
|
// Development tools
|
||||||
|
'postgrest/postgrest', // REST API for PostgreSQL
|
||||||
|
'supabase/postgres-meta', // PostgreSQL metadata API
|
||||||
|
'bluewaveuptime/uptime_redis', // Uptime monitoring with Redis
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($knownApplicationPatterns as $pattern) {
|
||||||
|
if (str($imageName)->contains($pattern)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for database-like ports (common database ports indicate it's likely a database)
|
||||||
|
$databasePorts = ['3306', '5432', '27017', '6379', '8086', '9200', '7687', '8123'];
|
||||||
|
$ports = data_get($serviceConfig, 'ports', []);
|
||||||
|
$hasStandardDbPort = false;
|
||||||
|
|
||||||
|
if (is_array($ports)) {
|
||||||
|
foreach ($ports as $port) {
|
||||||
|
$portStr = is_string($port) ? $port : (string) $port;
|
||||||
|
foreach ($databasePorts as $dbPort) {
|
||||||
|
if (str($portStr)->contains($dbPort)) {
|
||||||
|
$hasStandardDbPort = true;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check environment variables for database-specific patterns
|
||||||
|
$environment = data_get($serviceConfig, 'environment', []);
|
||||||
|
$hasDbEnvVars = false;
|
||||||
|
$hasAppEnvVars = false;
|
||||||
|
|
||||||
|
if (is_array($environment)) {
|
||||||
|
foreach ($environment as $env) {
|
||||||
|
$envStr = is_string($env) ? $env : (string) $env;
|
||||||
|
$envUpper = strtoupper($envStr);
|
||||||
|
|
||||||
|
// Database-specific environment variables
|
||||||
|
if (str($envUpper)->contains(['MYSQL_ROOT_PASSWORD', 'POSTGRES_PASSWORD', 'MONGO_INITDB_ROOT_PASSWORD', 'REDIS_PASSWORD'])) {
|
||||||
|
$hasDbEnvVars = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application-specific environment variables
|
||||||
|
if (str($envUpper)->contains(['SERVICE_FQDN', 'API_KEYS', 'APP_', 'APPLICATION_'])) {
|
||||||
|
$hasAppEnvVars = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check healthcheck patterns
|
||||||
|
$healthcheck = data_get($serviceConfig, 'healthcheck.test', []);
|
||||||
|
$hasDbHealthcheck = false;
|
||||||
|
$hasAppHealthcheck = false;
|
||||||
|
|
||||||
|
if (is_array($healthcheck)) {
|
||||||
|
$healthcheckStr = implode(' ', $healthcheck);
|
||||||
|
} else {
|
||||||
|
$healthcheckStr = is_string($healthcheck) ? $healthcheck : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($healthcheckStr)) {
|
||||||
|
$healthcheckUpper = strtoupper($healthcheckStr);
|
||||||
|
|
||||||
|
// Database-specific healthcheck patterns
|
||||||
|
if (str($healthcheckUpper)->contains(['PG_ISREADY', 'MYSQLADMIN PING', 'MONGO', 'REDIS-CLI PING'])) {
|
||||||
|
$hasDbHealthcheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application-specific healthcheck patterns (HTTP endpoints)
|
||||||
|
if (str($healthcheckUpper)->contains(['CURL', 'WGET', 'HTTP://', 'HTTPS://', '/HEALTH', '/API/', '/HELLO'])) {
|
||||||
|
$hasAppHealthcheck = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if service depends on other database services
|
||||||
|
$dependsOn = data_get($serviceConfig, 'depends_on', []);
|
||||||
|
$dependsOnDatabases = false;
|
||||||
|
|
||||||
|
if (is_array($dependsOn)) {
|
||||||
|
foreach ($dependsOn as $serviceName => $config) {
|
||||||
|
$serviceNameStr = is_string($serviceName) ? $serviceName : (string) $serviceName;
|
||||||
|
if (str($serviceNameStr)->contains(['mysql', 'postgres', 'mongo', 'redis', 'mariadb'])) {
|
||||||
|
$dependsOnDatabases = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decision logic:
|
||||||
|
// 1. If it has app-specific patterns and depends on databases, it's likely an application
|
||||||
|
if ($hasAppEnvVars && $dependsOnDatabases) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. If it has HTTP healthchecks, it's likely an application
|
||||||
|
if ($hasAppHealthcheck) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If it has standard database ports AND database healthchecks, it's likely a database
|
||||||
|
if ($hasStandardDbPort && $hasDbHealthcheck) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. If it has database environment variables, it's likely a database
|
||||||
|
if ($hasDbEnvVars) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Default: if it depends on databases but doesn't have database characteristics, it's an application
|
||||||
|
if ($dependsOnDatabases) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Fallback: assume it's a database if we can't determine otherwise
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertDockerRunToCompose(?string $custom_docker_run_options = null)
|
function convertDockerRunToCompose(?string $custom_docker_run_options = null)
|
||||||
|
@@ -1506,8 +1506,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
$containerName = "$serviceName-{$resource->uuid}";
|
$containerName = "$serviceName-{$resource->uuid}";
|
||||||
|
|
||||||
// Decide if the service is a database
|
// Decide if the service is a database
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
|
||||||
$image = data_get_str($service, 'image');
|
$image = data_get_str($service, 'image');
|
||||||
|
$isDatabase = isDatabaseImage($image, $service);
|
||||||
data_set($service, 'is_database', $isDatabase);
|
data_set($service, 'is_database', $isDatabase);
|
||||||
|
|
||||||
// Create new serviceApplication or serviceDatabase
|
// Create new serviceApplication or serviceDatabase
|
||||||
@@ -2494,7 +2494,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decide if the service is a database
|
// Decide if the service is a database
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
$image = data_get_str($service, 'image');
|
||||||
|
$isDatabase = isDatabaseImage($image, $service);
|
||||||
data_set($service, 'is_database', $isDatabase);
|
data_set($service, 'is_database', $isDatabase);
|
||||||
|
|
||||||
// Collect/create/update networks
|
// Collect/create/update networks
|
||||||
@@ -2965,7 +2966,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$environment = collect(data_get($service, 'environment', []));
|
$environment = collect(data_get($service, 'environment', []));
|
||||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||||
$environment = $environment->merge($buildArgs);
|
$environment = $environment->merge($buildArgs);
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
$isDatabase = isDatabaseImage($image, $service);
|
||||||
|
|
||||||
if ($isService) {
|
if ($isService) {
|
||||||
$containerName = "$serviceName-{$resource->uuid}";
|
$containerName = "$serviceName-{$resource->uuid}";
|
||||||
@@ -3214,7 +3215,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$environment = convertToKeyValueCollection($environment);
|
$environment = convertToKeyValueCollection($environment);
|
||||||
$coolifyEnvironments = collect([]);
|
$coolifyEnvironments = collect([]);
|
||||||
|
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
$isDatabase = isDatabaseImage($image, $service);
|
||||||
$volumesParsed = collect([]);
|
$volumesParsed = collect([]);
|
||||||
|
|
||||||
if ($isApplication) {
|
if ($isApplication) {
|
||||||
|
Reference in New Issue
Block a user