diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 473cbc679..0163ae013 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -157,7 +157,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue private ?string $coolify_variables = null; - private bool $preserveRepository = true; + private bool $preserveRepository = false; public $tries = 1; @@ -480,6 +480,7 @@ private function deploy_docker_compose_buildpack() } // Start compose file + $server_workdir = $this->application->workdir(); if ($this->application->settings->is_raw_compose_deployment_enabled) { if ($this->docker_compose_custom_start_command) { $this->write_deployment_configurations(); @@ -488,7 +489,6 @@ private function deploy_docker_compose_buildpack() ); } else { $this->write_deployment_configurations(); - $server_workdir = $this->application->workdir(); $this->docker_compose_location = '/docker-compose.yaml'; $command = "{$this->coolify_variables} docker compose"; @@ -508,15 +508,26 @@ private function deploy_docker_compose_buildpack() ); } else { $command = "{$this->coolify_variables} docker compose"; - if ($this->env_filename) { - $command .= " --env-file {$this->workdir}/{$this->env_filename}"; - } - $command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"; + if ($this->preserveRepository) { + if ($this->env_filename) { + $command .= " --env-file {$server_workdir}/{$this->env_filename}"; + } + $command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d"; + $this->write_deployment_configurations(); + + $this->execute_remote_command( + ['command' => $command, 'hidden' => true], + ); + } else { + if ($this->env_filename) { + $command .= " --env-file {$this->workdir}/{$this->env_filename}"; + } + $command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"; + $this->execute_remote_command( + [executeInDocker($this->deployment_uuid, $command), 'hidden' => true], + ); + } - $this->write_deployment_configurations(); - $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, $command), 'hidden' => true], - ); } } @@ -619,6 +630,11 @@ private function write_deployment_configurations() ], ); } + $this->application->fileStorages()->each(function ($fileStorage) { + if (! $fileStorage->is_based_on_git && ! $fileStorage->is_directory) { + $fileStorage->saveStorageOnServer(); + } + }); if ($this->use_build_server) { $this->server = $this->build_server; } diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 9b4c074c1..cf5f0979a 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -3,7 +3,6 @@ namespace App\Livewire\Project\Application; use App\Models\Application; -use App\Models\LocalFileVolume; use Illuminate\Support\Collection; use Livewire\Component; use Visus\Cuid2\Cuid2; @@ -30,6 +29,8 @@ class General extends Component public ?string $ports_exposes = null; + public bool $is_preserve_repository_enabled = false; + public bool $is_container_label_escape_enabled = true; public $customLabels; @@ -145,6 +146,7 @@ public function mount() } $this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : []; $this->ports_exposes = $this->application->ports_exposes; + $this->is_preserve_repository_enabled = $this->application->settings->is_preserve_repository_enabled; $this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled; $this->customLabels = $this->application->parseContainerLabels(); if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) { @@ -168,9 +170,21 @@ public function instantSave() $this->application->settings->save(); $this->dispatch('success', 'Settings saved.'); $this->application->refresh(); + + // If port_exposes changed, reset default labels if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) { $this->resetDefaultLabels(false); } + if ($this->is_preserve_repository_enabled !== $this->application->settings->is_preserve_repository_enabled) { + if ($this->application->settings->is_preserve_repository_enabled === false) { + $this->application->fileStorages->each(function ($storage) { + $storage->is_based_on_git = $this->application->settings->is_preserve_repository_enabled; + $storage->save(); + }); + } + + } + } public function loadComposeFile($isInit = false) @@ -191,32 +205,6 @@ public function loadComposeFile($isInit = false) return; } $compose = $this->application->parseCompose(); - $services = data_get($compose, 'services'); - if ($services) { - $volumes = collect($services)->map(function ($service) { - return data_get($service, 'volumes'); - })->flatten()->filter(function ($volume) { - return str($volume)->startsWith('/data/coolify'); - })->unique()->values(); - foreach ($volumes as $volume) { - $source = str($volume)->before(':'); - $target = str($volume)->after(':')->beforeLast(':'); - - LocalFileVolume::updateOrCreate( - [ - 'mount_path' => $target, - 'resource_id' => $this->application->id, - 'resource_type' => get_class($this->application), - ], - [ - 'fs_path' => $source, - 'mount_path' => $target, - 'resource_id' => $this->application->id, - 'resource_type' => get_class($this->application), - ] - ); - } - } $this->dispatch('success', 'Docker compose file loaded.'); $this->dispatch('compose_loaded'); $this->dispatch('refreshStorages'); diff --git a/app/Livewire/Project/Service/FileStorage.php b/app/Livewire/Project/Service/FileStorage.php index 2d9c95daa..438383a8d 100644 --- a/app/Livewire/Project/Service/FileStorage.php +++ b/app/Livewire/Project/Service/FileStorage.php @@ -33,6 +33,7 @@ class FileStorage extends Component 'fileStorage.fs_path' => 'required', 'fileStorage.mount_path' => 'required', 'fileStorage.content' => 'nullable', + 'fileStorage.is_based_on_git' => 'required|boolean', ]; public function mount() @@ -45,6 +46,7 @@ public function mount() $this->workdir = null; $this->fs_path = $this->fileStorage->fs_path; } + $this->fileStorage->loadStorageOnServer(); } public function convertToDirectory() @@ -68,6 +70,9 @@ public function convertToFile() $this->fileStorage->deleteStorageOnServer(); $this->fileStorage->is_directory = false; $this->fileStorage->content = null; + if (data_get($this->resource, 'settings.is_preserve_repository_enabled')) { + $this->fileStorage->is_based_on_git = true; + } $this->fileStorage->save(); $this->fileStorage->saveStorageOnServer(); } catch (\Throwable $e) { diff --git a/app/Models/LocalFileVolume.php b/app/Models/LocalFileVolume.php index a436f5797..0fe48c5c4 100644 --- a/app/Models/LocalFileVolume.php +++ b/app/Models/LocalFileVolume.php @@ -24,6 +24,32 @@ public function service() return $this->morphTo('resource'); } + public function loadStorageOnServer() + { + $this->load(['service']); + $isService = data_get($this->resource, 'service'); + if ($isService) { + $workdir = $this->resource->service->workdir(); + $server = $this->resource->service->server; + } else { + $workdir = $this->resource->workdir(); + $server = $this->resource->destination->server; + } + $commands = collect([]); + $path = data_get_str($this, 'fs_path'); + if ($path->startsWith('.')) { + $path = $path->after('.'); + $path = $workdir.$path; + } + $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server); + if ($isFile === 'OK') { + $content = instant_remote_process(["cat $path"], $server, false); + $this->content = $content; + $this->is_directory = false; + $this->save(); + } + } + public function deleteStorageOnServer() { $isService = data_get($this->resource, 'service'); @@ -35,17 +61,20 @@ public function deleteStorageOnServer() $server = $this->resource->destination->server; } $commands = collect([]); - $fs_path = data_get($this, 'fs_path'); - $isFile = instant_remote_process(["test -f $fs_path && echo OK || echo NOK"], $server); - $isDir = instant_remote_process(["test -d $fs_path && echo OK || echo NOK"], $server); - if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') { - ray($isFile, $isDir); + $path = data_get_str($this, 'fs_path'); + if ($path->startsWith('.')) { + $path = $path->after('.'); + $path = $workdir.$path; + } + $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server); + $isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server); + if ($path && $path != '/' && $path != '.' && $path != '..') { if ($isFile === 'OK') { - $commands->push("rm -rf $fs_path > /dev/null 2>&1 || true"); + $commands->push("rm -rf $path > /dev/null 2>&1 || true"); } elseif ($isDir === 'OK') { - $commands->push("rm -rf $fs_path > /dev/null 2>&1 || true"); - $commands->push("rmdir $fs_path > /dev/null 2>&1 || true"); + $commands->push("rm -rf $path > /dev/null 2>&1 || true"); + $commands->push("rmdir $path > /dev/null 2>&1 || true"); } } if ($commands->count() > 0) { @@ -55,6 +84,7 @@ public function deleteStorageOnServer() public function saveStorageOnServer() { + $this->load(['service']); $isService = data_get($this->resource, 'service'); if ($isService) { $workdir = $this->resource->service->workdir(); @@ -74,30 +104,36 @@ public function saveStorageOnServer() $commands->push("mkdir -p $parent_dir > /dev/null 2>&1 || true"); } } - $fileVolume = $this; - $path = str(data_get($fileVolume, 'fs_path')); - $content = data_get($fileVolume, 'content'); + $path = data_get_str($this, 'fs_path'); + $content = data_get($this, 'content'); if ($path->startsWith('.')) { $path = $path->after('.'); $path = $workdir.$path; } $isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server); $isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server); - if ($isFile == 'OK' && $fileVolume->is_directory) { + if ($isFile == 'OK' && $this->is_directory) { $content = instant_remote_process(["cat $path"], $server, false); - $fileVolume->is_directory = false; - $fileVolume->content = $content; - $fileVolume->save(); + $this->is_directory = false; + $this->content = $content; + $this->save(); FileStorageChanged::dispatch(data_get($server, 'team_id')); throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.'); - } elseif ($isDir == 'OK' && ! $fileVolume->is_directory) { - $fileVolume->is_directory = true; - $fileVolume->save(); - throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file.

Please delete the directory on the server or mark it as directory.'); + } elseif ($isDir == 'OK' && ! $this->is_directory) { + if ($path == '/' || $path == '.' || $path == '..' || $path == '' || str($path)->isEmpty() || is_null($path)) { + $this->is_directory = true; + $this->save(); + throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file.

Please delete the directory on the server or mark it as directory.'); + } + instant_remote_process([ + "rm -fr $path", + "touch $path", + ], $server, false); + FileStorageChanged::dispatch(data_get($server, 'team_id')); } - if ($isDir == 'NOK' && ! $fileVolume->is_directory) { - $chmod = data_get($fileVolume, 'chmod'); - $chown = data_get($fileVolume, 'chown'); + if ($isDir == 'NOK' && ! $this->is_directory) { + $chmod = data_get($this, 'chmod'); + $chown = data_get($this, 'chown'); if ($content) { $content = base64_encode($content); $commands->push("echo '$content' | base64 -d | tee $path > /dev/null"); @@ -111,7 +147,7 @@ public function saveStorageOnServer() if ($chmod) { $commands->push("chmod $chmod $path"); } - } elseif ($isDir == 'NOK' && $fileVolume->is_directory) { + } elseif ($isDir == 'NOK' && $this->is_directory) { $commands->push("mkdir -p $path > /dev/null 2>&1 || true"); } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index aae4fafd4..55ecf223c 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -794,7 +794,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } $topLevelVolumes = collect($tempTopLevelVolumes); } - $services = collect($services)->map(function ($service, $serviceName) use ($topLevelVolumes, $topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $allServices) { + $services = collect($services)->map(function ($service, $serviceName) use ($topLevelNetworks, $definedNetwork, $isNew, $generatedServiceFQDNS, $resource, $allServices, $topLevelVolumes) { // Workarounds for beta users. if ($serviceName === 'registry') { $tempServiceName = 'docker-registry'; @@ -963,102 +963,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal // Collect/create/update volumes if ($serviceVolumes->count() > 0) { - $serviceVolumes = $serviceVolumes->map(function ($volume) use ($savedService, $topLevelVolumes) { - $type = null; - $source = null; - $target = null; - $content = null; - $isDirectory = false; - if (is_string($volume)) { - $source = str($volume)->before(':'); - $target = str($volume)->after(':')->beforeLast(':'); - if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) { - $type = str('bind'); - // By default, we cannot determine if the bind is a directory or not, so we set it to directory - $isDirectory = true; - } else { - $type = str('volume'); - } - } elseif (is_array($volume)) { - $type = data_get_str($volume, 'type'); - $source = data_get_str($volume, 'source'); - $target = data_get_str($volume, 'target'); - $content = data_get($volume, 'content'); - $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null); - $foundConfig = $savedService->fileStorages()->whereMountPath($target)->first(); - if ($foundConfig) { - $contentNotNull = data_get($foundConfig, 'content'); - if ($contentNotNull) { - $content = $contentNotNull; - } - $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null); - } - if (is_null($isDirectory) && is_null($content)) { - // if isDirectory is not set & content is also not set, we assume it is a directory - ray('setting isDirectory to true'); - $isDirectory = true; - } - } - if ($type?->value() === 'bind') { - if ($source->value() === '/var/run/docker.sock') { - return $volume; - } - if ($source->value() === '/tmp' || $source->value() === '/tmp/') { - return $volume; - } - LocalFileVolume::updateOrCreate( - [ - 'mount_path' => $target, - 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService), - ], - [ - 'fs_path' => $source, - 'mount_path' => $target, - 'content' => $content, - 'is_directory' => $isDirectory, - 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService), - ] - ); - } elseif ($type->value() === 'volume') { - if ($topLevelVolumes->has($source->value())) { - $v = $topLevelVolumes->get($source->value()); - if (data_get($v, 'driver_opts.type') === 'cifs') { - return $volume; - } - } - $slugWithoutUuid = Str::slug($source, '-'); - $name = "{$savedService->service->uuid}_{$slugWithoutUuid}"; - if (is_string($volume)) { - $source = str($volume)->before(':'); - $target = str($volume)->after(':')->beforeLast(':'); - $source = $name; - $volume = "$source:$target"; - } elseif (is_array($volume)) { - data_set($volume, 'source', $name); - } - $topLevelVolumes->put($name, [ - 'name' => $name, - ]); - LocalPersistentVolume::updateOrCreate( - [ - 'mount_path' => $target, - 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService), - ], - [ - 'name' => $name, - 'mount_path' => $target, - 'resource_id' => $savedService->id, - 'resource_type' => get_class($savedService), - ] - ); - } - dispatch(new ServerFilesFromServerJob($savedService)); - - return $volume; - }); + $serviceVolumes = parseServiceVolumes($serviceVolumes, $savedService, $topLevelVolumes); data_set($service, 'volumes', $serviceVolumes->toArray()); } @@ -1645,131 +1550,261 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } elseif ($resource->compose_parsing_version === '2') { if (count($serviceVolumes) > 0) { - $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes, $pull_request_id) { - if (is_string($volume)) { - $volume = str($volume); - if ($volume->contains(':') && ! $volume->startsWith('/')) { - $name = $volume->before(':'); - $mount = $volume->after(':'); - if ($name->startsWith('.') || $name->startsWith('~')) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; - if ($name->startsWith('.')) { - $name = $name->replaceFirst('.', $dir); - } - if ($name->startsWith('~')) { - $name = $name->replaceFirst('~', $dir); - } - if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; - } - $volume = str("$name:$mount"); - } else { - if ($pull_request_id !== 0) { - $uuid = $resource->uuid; - $name = $uuid."-$name-pr-$pull_request_id"; - $volume = str("$name:$mount"); - if ($topLevelVolumes->has($name)) { - $v = $topLevelVolumes->get($name); - if (data_get($v, 'driver_opts.type') === 'cifs') { - // Do nothing - } else { - if (is_null(data_get($v, 'name'))) { - data_set($v, 'name', $name); - data_set($topLevelVolumes, $name, $v); - } - } - } else { - $topLevelVolumes->put($name, [ - 'name' => $name, - ]); - } - } else { - $uuid = $resource->uuid; - $name = str($uuid."-$name"); - $volume = str("$name:$mount"); - if ($topLevelVolumes->has($name->value())) { - $v = $topLevelVolumes->get($name->value()); - if (data_get($v, 'driver_opts.type') === 'cifs') { - // Do nothing - } else { - if (is_null(data_get($v, 'name'))) { - data_set($topLevelVolumes, $name->value(), $v); - } - } - } else { - $topLevelVolumes->put($name->value(), [ - 'name' => $name->value(), - ]); - } - } - } - } else { - if ($volume->startsWith('/')) { - $name = $volume->before(':'); - $mount = $volume->after(':'); - if ($pull_request_id !== 0) { - $name = $name."-pr-$pull_request_id"; - } - $volume = str("$name:$mount"); - } - } - } elseif (is_array($volume)) { - $source = data_get($volume, 'source'); - $target = data_get($volume, 'target'); - $read_only = data_get($volume, 'read_only'); - if ($source && $target) { - $uuid = $resource->uuid; - if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) { - $dir = base_configuration_dir().'/applications/'.$resource->uuid; - if (str($source, '.')) { - $source = str($source)->replaceFirst('.', $dir); - } - if (str($source, '~')) { - $source = str($source)->replaceFirst('~', $dir); - } - if ($read_only) { - data_set($volume, 'source', $source.':'.$target.':ro'); - } else { - data_set($volume, 'source', $source.':'.$target); - } - } else { - if ($pull_request_id === 0) { - $source = $uuid."-$source"; - } else { - $source = $uuid."-$source-pr-$pull_request_id"; - } - if ($read_only) { - data_set($volume, 'source', $source.':'.$target.':ro'); - } else { - data_set($volume, 'source', $source.':'.$target); - } - if (! str($source)->startsWith('/')) { - if ($topLevelVolumes->has($source)) { - $v = $topLevelVolumes->get($source); - if (data_get($v, 'driver_opts.type') === 'cifs') { - // Do nothing - } else { - if (is_null(data_get($v, 'name'))) { - data_set($v, 'name', $source); - data_set($topLevelVolumes, $source, $v); - } - } - } else { - $topLevelVolumes->put($source, [ - 'name' => $source, - ]); - } - } - } - } - } - if (is_array($volume)) { - return data_get($volume, 'source'); - } - dispatch(new ServerFilesFromServerJob($resource)); + $serviceVolumes = parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull_request_id); + ray($serviceVolumes); - return $volume->value(); - }); + data_set($service, 'volumes', $serviceVolumes->toArray()); + // $serviceVolumes = $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes, $pull_request_id) { + // if (is_string($volume)) { + // $volume = str($volume); + // if ($volume->contains(':')) { + // $name = $volume->before(':'); + // $mount = $volume->after(':')->beforeLast(':'); + // if ($name->startsWith('.') || $name->startsWith('~') || $name->startsWith('/')) { + // // File or dir mount from the host system + // $dir = base_configuration_dir().'/applications/'.$resource->uuid; + // if ($name->startsWith('.')) { + // $name = $name->replaceFirst('.', $dir); + // } + // if ($name->startsWith('~')) { + // $name = $name->replaceFirst('~', $dir); + // } + // if ($pull_request_id !== 0) { + // $name = $name."-pr-$pull_request_id"; + // } + + // $volume = str("$name:$mount"); + // LocalFileVolume::updateOrCreate( + // [ + // 'mount_path' => $mount, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ], + // [ + // 'fs_path' => $name, + // 'mount_path' => $mount, + // 'is_directory' => true, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ] + // ); + // } else { + // // Docker Volume part + // if ($pull_request_id == 0) { + // $uuid = $resource->uuid; + // $name = str($uuid."-$name"); + // $volume = str("$name:$mount"); + // if ($topLevelVolumes->has($name->value())) { + // $v = $topLevelVolumes->get($name->value()); + // if (data_get($v, 'driver_opts.type') === 'cifs') { + // // Do nothing + // } else { + // if (is_null(data_get($v, 'name'))) { + // data_set($topLevelVolumes, $name->value(), $v); + // } + // } + // } else { + // $topLevelVolumes->put($name->value(), [ + // 'name' => $name->value(), + // ]); + // } + // LocalPersistentVolume::updateOrCreate( + // [ + // 'mount_path' => $mount, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ], + // [ + // 'name' => $name, + // 'mount_path' => $mount, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ] + // ); + // } else { + // $uuid = $resource->uuid; + // $name = $uuid."-$name-pr-$pull_request_id"; + // $volume = str("$name:$mount"); + // if ($topLevelVolumes->has($name)) { + // $v = $topLevelVolumes->get($name); + // if (data_get($v, 'driver_opts.type') === 'cifs') { + // // Do nothing + // } else { + // if (is_null(data_get($v, 'name'))) { + // data_set($v, 'name', $name); + // data_set($topLevelVolumes, $name, $v); + // } + // } + // } else { + // $topLevelVolumes->put($name, [ + // 'name' => $name, + // ]); + // } + // LocalPersistentVolume::updateOrCreate( + // [ + // 'mount_path' => $mount, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ], + // [ + // 'name' => $name, + // 'mount_path' => $mount, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ] + // ); + // } + + // } + // } + // } elseif (is_array($volume)) { + // $source = data_get($volume, 'source'); + // $target = data_get($volume, 'target'); + // $type = data_get($volume, 'type'); + // $read_only = data_get($volume, 'read_only'); + // $content = data_get_str($volume, 'content'); + // if ($source && $target) { + // if ($type?->value() === 'bind') { + // if ($source->value() === '/var/run/docker.sock') { + // return $volume; + // } + // if ($source->value() === '/tmp' || $source->value() === '/tmp/') { + // return $volume; + // } + // LocalFileVolume::updateOrCreate( + // [ + // 'mount_path' => $target, + // 'resource_id' => $savedService->id, + // 'resource_type' => get_class($savedService), + // ], + // [ + // 'fs_path' => $source, + // 'mount_path' => $target, + // 'content' => $content, + // 'is_directory' => $isDirectory, + // 'resource_id' => $savedService->id, + // 'resource_type' => get_class($savedService), + // ] + // ); + // } elseif ($type->value() === 'volume') { + // if ($topLevelVolumes->has($source->value())) { + // $v = $topLevelVolumes->get($source->value()); + // if (data_get($v, 'driver_opts.type') === 'cifs') { + // return $volume; + // } + // } + // $slugWithoutUuid = Str::slug($source, '-'); + // $name = "{$savedService->service->uuid}_{$slugWithoutUuid}"; + // if (is_string($volume)) { + // $source = str($volume)->before(':'); + // $target = str($volume)->after(':')->beforeLast(':'); + // $source = $name; + // $volume = "$source:$target"; + // } elseif (is_array($volume)) { + // data_set($volume, 'source', $name); + // } + // $topLevelVolumes->put($name, [ + // 'name' => $name, + // ]); + // LocalPersistentVolume::updateOrCreate( + // [ + // 'mount_path' => $target, + // 'resource_id' => $savedService->id, + // 'resource_type' => get_class($savedService), + // ], + // [ + // 'name' => $name, + // 'mount_path' => $target, + // 'resource_id' => $savedService->id, + // 'resource_type' => get_class($savedService), + // ] + // ); + // } + + // $uuid = $resource->uuid; + // if ((str($source)->startsWith('.') || str($source)->startsWith('~') || str($source)->startsWith('/'))) { + // $dir = base_configuration_dir().'/applications/'.$resource->uuid; + // if (str($source, '.')) { + // $source = str($source)->replaceFirst('.', $dir); + // } + // if (str($source, '~')) { + // $source = str($source)->replaceFirst('~', $dir); + // } + // if ($read_only) { + // data_set($volume, 'source', $source.':'.$target.':ro'); + // } else { + // data_set($volume, 'source', $source.':'.$target); + // } + // } else { + // if ($pull_request_id === 0) { + // $source = $uuid."-$source"; + // } else { + // $source = $uuid."-$source-pr-$pull_request_id"; + // } + // if ($read_only) { + // data_set($volume, 'source', $source.':'.$target.':ro'); + // } else { + // data_set($volume, 'source', $source.':'.$target); + // } + // if (! str($source)->startsWith('/')) { + // if ($topLevelVolumes->has($source)) { + // $v = $topLevelVolumes->get($source); + // if (data_get($v, 'driver_opts.type') === 'cifs') { + // // Do nothing + // } else { + // if (is_null(data_get($v, 'name'))) { + // data_set($v, 'name', $source); + // data_set($topLevelVolumes, $source, $v); + // } + // } + // } else { + // $topLevelVolumes->put($source, [ + // 'name' => $source, + // ]); + // } + // } + // } + // if ($content->isNotEmpty()) { + // LocalFileVolume::updateOrCreate( + // [ + // 'mount_path' => $target, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ], + // [ + // 'fs_path' => $source, + // 'mount_path' => $target, + // 'content' => $content, + // 'is_directory' => false, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ] + // ); + // } else { + // LocalFileVolume::updateOrCreate( + // [ + // 'mount_path' => $target, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ], + // [ + // 'fs_path' => $source, + // 'mount_path' => $target, + // 'is_directory' => true, + // 'resource_id' => $resource->id, + // 'resource_type' => get_class($resource), + // ] + // ); + // } + // } + // } + // if (is_array($volume)) { + // return data_get($volume, 'source'); + // } + // dispatch(new ServerFilesFromServerJob($resource)); + + // return $volume->value(); + // }); data_set($service, 'volumes', $serviceVolumes->toArray()); } } @@ -2662,3 +2697,124 @@ function customApiValidator(Collection|array $item, array $rules) 'required' => 'This field is required.', ]); } + +function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull_request_id = 0) +{ + return $serviceVolumes->map(function ($volume) use ($resource, $topLevelVolumes, $pull_request_id) { + $type = null; + $source = null; + $target = null; + $content = null; + $isDirectory = false; + if (is_string($volume)) { + $source = str($volume)->before(':'); + $target = str($volume)->after(':')->beforeLast(':'); + if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) { + $type = str('bind'); + // By default, we cannot determine if the bind is a directory or not, so we set it to directory + $isDirectory = true; + } else { + $type = str('volume'); + } + } elseif (is_array($volume)) { + $type = data_get_str($volume, 'type'); + $source = data_get_str($volume, 'source'); + $target = data_get_str($volume, 'target'); + $content = data_get($volume, 'content'); + $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null); + $foundConfig = $resource->fileStorages()->whereMountPath($target)->first(); + if ($foundConfig) { + $contentNotNull = data_get($foundConfig, 'content'); + if ($contentNotNull) { + $content = $contentNotNull; + } + $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null); + } + if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) { + // if isDirectory is not set (or false) & content is also not set, we assume it is a directory + ray('setting isDirectory to true'); + $isDirectory = true; + } + } + if ($type?->value() === 'bind') { + if ($source->value() === '/var/run/docker.sock') { + return $volume; + } + if ($source->value() === '/tmp' || $source->value() === '/tmp/') { + return $volume; + } + if (get_class($resource) === "App\Models\Application") { + $dir = base_configuration_dir().'/applications/'.$resource->uuid; + } else { + $dir = base_configuration_dir().'/services/'.$resource->service->uuid; + } + + if ($source->startsWith('.')) { + $source = $source->replaceFirst('.', $dir); + } + if ($source->startsWith('~')) { + $source = $source->replaceFirst('~', $dir); + } + if ($pull_request_id !== 0) { + $source = $source."-pr-$pull_request_id"; + } + + $volume = str("$source:$target"); + LocalFileVolume::updateOrCreate( + [ + 'mount_path' => $target, + 'resource_id' => $resource->id, + 'resource_type' => get_class($resource), + ], + [ + 'fs_path' => $source, + 'mount_path' => $target, + 'content' => $content, + 'is_directory' => $isDirectory, + 'resource_id' => $resource->id, + 'resource_type' => get_class($resource), + ] + ); + } elseif ($type->value() === 'volume') { + if ($topLevelVolumes->has($source->value())) { + $v = $topLevelVolumes->get($source->value()); + if (data_get($v, 'driver_opts.type') === 'cifs') { + return $volume; + } + } + $slugWithoutUuid = Str::slug($source, '-'); + if (get_class($resource) === "App\Models\Application") { + $name = "{$resource->uuid}_{$slugWithoutUuid}"; + } else { + $name = "{$resource->service->uuid}_{$slugWithoutUuid}"; + } + if (is_string($volume)) { + $source = str($volume)->before(':'); + $target = str($volume)->after(':')->beforeLast(':'); + $source = $name; + $volume = "$source:$target"; + } elseif (is_array($volume)) { + data_set($volume, 'source', $name); + } + $topLevelVolumes->put($name, [ + 'name' => $name, + ]); + LocalPersistentVolume::updateOrCreate( + [ + 'mount_path' => $target, + 'resource_id' => $resource->id, + 'resource_type' => get_class($resource), + ], + [ + 'name' => $name, + 'mount_path' => $target, + 'resource_id' => $resource->id, + 'resource_type' => get_class($resource), + ] + ); + } + dispatch(new ServerFilesFromServerJob($resource)); + + return str($volume)->value(); + }); +} diff --git a/database/migrations/2024_08_12_131659_add_local_file_volume_based_on_git.php b/database/migrations/2024_08_12_131659_add_local_file_volume_based_on_git.php new file mode 100644 index 000000000..d180c7ec2 --- /dev/null +++ b/database/migrations/2024_08_12_131659_add_local_file_volume_based_on_git.php @@ -0,0 +1,28 @@ +boolean('is_based_on_git')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('local_file_volumes', function (Blueprint $table) { + $table->dropColumn('is_based_on_git'); + }); + } +}; diff --git a/resources/views/livewire/project/service/file-storage.blade.php b/resources/views/livewire/project/service/file-storage.blade.php index 96590cada..78cf70429 100644 --- a/resources/views/livewire/project/service/file-storage.blade.php +++ b/resources/views/livewire/project/service/file-storage.blade.php @@ -10,6 +10,7 @@ @endif
{{ $workdir }}{{ $fs_path }} -> {{ $fileStorage->mount_path }}
+
@if ($fileStorage->is_directory) @@ -26,25 +27,40 @@ class="text-error">Please think
@endif - -
This storage will be deleted. It is not reversible. Please - think - again.

-

Actions

- @if ($fileStorage->is_directory) - - @else - - @endif -
+ + @if (!$fileStorage->is_based_on_git) + +
This storage will be deleted. It is not reversible. Please + think + again.

+

Actions

+ @if ($fileStorage->is_directory) + + @else + + @endif +
+ @endif @if (!$fileStorage->is_directory) - - Save + @if (data_get($resource, 'settings.is_preserve_repository_enabled')) +
+ +
+ @endif + + @if (!$fileStorage->is_based_on_git) + Save + @endif @endif
+