mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 21:18:11 -04:00
fix(storage): stop syncing file storages on every compose parse
The compose parser queued ServerFilesFromServerJob for every volume on each parse, and pages that only show data parse the Compose file. Each sync also sent two SSH commands for each file. - Queue the sync only for a compose deployment, and for each service resource with file storages when the service starts or deploys. - Read the compose service names without a parse on the Domains and scheduled task pages. - Check all file storages with one remote command, and stop when a symbolic link, a special file, or an unknown state is at a file path. - Keep the application path in a file storage when a pull request preview is parsed. Preview deployments of the current parser write their `-pr-<id>` copies through LocalFileVolume::forPullRequest(). - Remove the unreachable file sync from the service resource page.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Jobs\ServerFilesFromServerJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\LocalFileVolume;
|
||||
@@ -1602,6 +1601,8 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
}
|
||||
$source = resolveComposeBindSource($source, $mainDirectory, $foundConfig?->fs_path);
|
||||
// The file storage keeps the path of the application; a preview deployment adds the suffix (LocalFileVolume::forPullRequest()).
|
||||
$storedSource = $source;
|
||||
$isPreviewSuffixEnabled = $foundConfig
|
||||
? (bool) data_get($foundConfig, 'is_preview_suffix_enabled', true)
|
||||
: true;
|
||||
@@ -1617,7 +1618,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
'resource_type' => get_class($originalResource),
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'fs_path' => $storedSource,
|
||||
'mount_path' => $target,
|
||||
'content' => $content,
|
||||
'is_directory' => $isDirectory,
|
||||
@@ -1698,7 +1699,6 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
? ['name' => $name]
|
||||
: composeRenamedVolumeDeclarationFor($declaration, $name, $persistentVolume, $isPullRequest));
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($originalResource));
|
||||
$volumesParsed->put($index, $volume);
|
||||
}
|
||||
}
|
||||
@@ -3121,7 +3121,6 @@ function serviceParser(Service $resource): Collection
|
||||
);
|
||||
$topLevel->get('volumes')->put($name, composeRenamedVolumeDeclarationFor($declaration, $name, $persistentVolume));
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($originalResource));
|
||||
$volumesParsed->put($index, $volume);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,45 +301,53 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
|
||||
$server = $oneService->service->server;
|
||||
}
|
||||
$fileVolumes = $oneService->fileStorages()->get();
|
||||
$escapedWorkdir = escapeshellarg($workdir);
|
||||
$commands = collect([
|
||||
"mkdir -p -- {$escapedWorkdir} > /dev/null 2>&1 || true",
|
||||
]);
|
||||
instant_remote_process($commands, $server);
|
||||
foreach ($fileVolumes as $fileVolume) {
|
||||
$content = data_get($fileVolume, 'content');
|
||||
// A relative path is inside the resource directory, never in the SSH user's working
|
||||
// directory. A persisted Compose expression (`${VAR:-/path}`) stays as it is.
|
||||
if ($isInit) {
|
||||
instant_remote_process(['mkdir -p -- '.escapeshellarg($workdir).' > /dev/null 2>&1 || true'], $server);
|
||||
}
|
||||
if ($fileVolumes->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// A relative path is inside the resource directory, never in the SSH user's working
|
||||
// directory. A persisted Compose expression (`${VAR:-/path}`) stays as it is.
|
||||
$fileLocations = $fileVolumes->map(function (LocalFileVolume $fileVolume) use ($workdir): string {
|
||||
$fsPath = trim((string) $fileVolume->fs_path);
|
||||
$fileLocation = str_starts_with($fsPath, '$') ? $fsPath : $fileVolume->resolvedFsPath($workdir)->value();
|
||||
$escapedFileLocation = filesystemVolumeShellArgument((string) $fileLocation);
|
||||
|
||||
return str_starts_with($fsPath, '$') ? $fsPath : $fileVolume->resolvedFsPath($workdir)->value();
|
||||
})->values()->all();
|
||||
$states = LocalFileVolume::remoteFileStates($fileLocations, $server, filesystemVolumeShellArgument(...));
|
||||
foreach ($fileVolumes->values() as $index => $fileVolume) {
|
||||
$content = data_get($fileVolume, 'content');
|
||||
$fileLocation = $fileLocations[$index];
|
||||
$escapedFileLocation = filesystemVolumeShellArgument($fileLocation);
|
||||
// A token with only the write permission can save a Compose file, so Coolify does not
|
||||
// write outside the resource directory here. A deployment or start writes those paths.
|
||||
$isOutsideResourceDirectory = fn (): bool => $fileVolume->isOutsideResourceDirectory();
|
||||
// Exists and is a file
|
||||
$isFile = instant_remote_process(["test -f {$escapedFileLocation} && echo OK || echo NOK"], $server);
|
||||
// Exists and is a directory
|
||||
$isDir = instant_remote_process(["test -d {$escapedFileLocation} && echo OK || echo NOK"], $server);
|
||||
$state = $states[$index];
|
||||
// A symbolic link to a directory, a special file, or a failed check: Coolify changes nothing.
|
||||
if ($state === 'other' || $state === 'unknown') {
|
||||
continue;
|
||||
}
|
||||
$isDir = in_array($state, ['directory', 'empty-directory'], true);
|
||||
|
||||
if ($isFile === 'OK') {
|
||||
if ($state === 'file') {
|
||||
$fileVolume->is_directory = false;
|
||||
$fileVolume->save();
|
||||
if ($fileVolume->is_based_on_git) {
|
||||
$fileVolume->loadStorageOnServer();
|
||||
}
|
||||
} elseif ($isDir === 'OK' && ! $fileVolume->is_directory && filled($content)) {
|
||||
} elseif ($isDir && ! $fileVolume->is_directory && filled($content)) {
|
||||
// A configured file must not lose its content because a directory is at its path.
|
||||
// Docker leaves an empty directory when it starts before the file exists: replace it.
|
||||
// A directory with files stays; the start or deployment shows a warning for it.
|
||||
if (! $isOutsideResourceDirectory() && LocalFileVolume::remoteFileStates([(string) $fileLocation], $server)[0] === 'empty-directory') {
|
||||
if (! $isOutsideResourceDirectory() && $state === 'empty-directory') {
|
||||
$fileVolume->saveStorageOnServer();
|
||||
}
|
||||
} elseif ($isDir === 'OK') {
|
||||
} elseif ($isDir) {
|
||||
// If its a directory & exists
|
||||
$fileVolume->content = null;
|
||||
$fileVolume->is_directory = true;
|
||||
$fileVolume->save();
|
||||
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && ! $fileVolume->is_directory && $isInit && $content) {
|
||||
} elseif ($state === 'missing' && ! $fileVolume->is_directory && $isInit && $content) {
|
||||
// Does not exists (no dir or file), not flagged as directory, is init, has content
|
||||
if ($isOutsideResourceDirectory()) {
|
||||
continue;
|
||||
@@ -352,7 +360,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
|
||||
'mkdir -p -- "$(dirname -- '.$escapedFileLocation.')"',
|
||||
"echo '$content' | base64 -d | tee -- {$escapedFileLocation}",
|
||||
], $server);
|
||||
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && $fileVolume->is_directory && $isInit) {
|
||||
} elseif ($state === 'missing' && $fileVolume->is_directory && $isInit) {
|
||||
// Does not exists (no dir or file), flagged as directory, is init
|
||||
$fileVolume->content = null;
|
||||
$fileVolume->is_directory = true;
|
||||
@@ -360,7 +368,7 @@ function getFilesystemVolumesFromServer(ServiceApplication|ServiceDatabase|Appli
|
||||
if (! $isOutsideResourceDirectory()) {
|
||||
instant_remote_process(["mkdir -p -- {$escapedFileLocation}"], $server);
|
||||
}
|
||||
} elseif ($isFile === 'NOK' && $isDir === 'NOK' && ! $fileVolume->is_directory && $isInit && is_null($content)) {
|
||||
} elseif ($state === 'missing' && ! $fileVolume->is_directory && $isInit && is_null($content)) {
|
||||
// Does not exists (no dir or file), not flagged as directory, is init, has no content => create directory
|
||||
$fileVolume->content = null;
|
||||
$fileVolume->is_directory = true;
|
||||
|
||||
Reference in New Issue
Block a user