diff --git a/app/Jobs/VolumeCloneJob.php b/app/Jobs/VolumeCloneJob.php index 95ac14197..f37a9704e 100644 --- a/app/Jobs/VolumeCloneJob.php +++ b/app/Jobs/VolumeCloneJob.php @@ -15,10 +15,13 @@ class VolumeCloneJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + protected string $cloneDir = '/data/coolify/clone'; + public function __construct( protected string $sourceVolume, protected string $targetVolume, - protected Server $server, + protected Server $sourceServer, + protected ?Server $targetServer, protected LocalPersistentVolume $persistentVolume ) { $this->onQueue('high'); @@ -27,13 +30,75 @@ class VolumeCloneJob implements ShouldBeEncrypted, ShouldQueue public function handle() { try { - instant_remote_process([ - "docker volume create $this->targetVolume", - "docker run --rm -v $this->sourceVolume:/source -v $this->targetVolume:/target alpine sh -c 'cp -a /source/. /target/ && chown -R 1000:1000 /target'", - ], $this->server); + if (! $this->targetServer || $this->targetServer->id === $this->sourceServer->id) { + $this->cloneLocalVolume(); + } else { + $this->cloneRemoteVolume(); + } } catch (\Exception $e) { - logger()->error("Failed to copy volume data for {$this->sourceVolume}: ".$e->getMessage()); + \Log::error("Failed to copy volume data for {$this->sourceVolume}: ".$e->getMessage()); throw $e; } } + + protected function cloneLocalVolume() + { + instant_remote_process([ + "docker volume create $this->targetVolume", + "docker run --rm -v $this->sourceVolume:/source -v $this->targetVolume:/target alpine sh -c 'cp -a /source/. /target/ && chown -R 1000:1000 /target'", + ], $this->sourceServer); + } + + protected function cloneRemoteVolume() + { + $sourceCloneDir = "{$this->cloneDir}/{$this->sourceVolume}"; + $targetCloneDir = "{$this->cloneDir}/{$this->targetVolume}"; + + try { + instant_remote_process([ + "mkdir -p $sourceCloneDir", + "chmod 777 $sourceCloneDir", + "docker run --rm -v $this->sourceVolume:/source -v $sourceCloneDir:/clone alpine sh -c 'cd /source && tar czf /clone/volume-data.tar.gz .'", + ], $this->sourceServer); + + instant_remote_process([ + "mkdir -p $targetCloneDir", + "chmod 777 $targetCloneDir", + ], $this->targetServer); + + instant_scp( + "$sourceCloneDir/volume-data.tar.gz", + "$targetCloneDir/volume-data.tar.gz", + $this->sourceServer, + $this->targetServer + ); + + instant_remote_process([ + "docker volume create $this->targetVolume", + "docker run --rm -v $this->targetVolume:/target -v $targetCloneDir:/clone alpine sh -c 'cd /target && tar xzf /clone/volume-data.tar.gz && chown -R 1000:1000 /target'", + ], $this->targetServer); + + } catch (\Exception $e) { + \Log::error("Failed to clone volume {$this->sourceVolume} to {$this->targetVolume}: ".$e->getMessage()); + throw $e; + } finally { + try { + instant_remote_process([ + "rm -rf $sourceCloneDir", + ], $this->sourceServer, false); + } catch (\Exception $e) { + \Log::warning('Failed to clean up source server clone directory: '.$e->getMessage()); + } + + try { + if ($this->targetServer) { + instant_remote_process([ + "rm -rf $targetCloneDir", + ], $this->targetServer, false); + } + } catch (\Exception $e) { + \Log::warning('Failed to clean up target server clone directory: '.$e->getMessage()); + } + } + } }